aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/work.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-12-23 07:15:43 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-28 16:55:05 -0500
commite4da8c37af626001ff704fb29ea14eb58f5f7208 (patch)
treed56bf3a225979b18370183eb9c39f66d8e524069 /net/mac80211/work.c
parentb8bc4b0aa9bfba755c64b11b8f60e6cfab25dc9d (diff)
mac80211: make off-channel work generic
This changes mac80211 to allow being off-channel for any type of work, not just the 'remain-on-channel' work. This also helps fast transition to a BSS on a different channel. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r--net/mac80211/work.c135
1 files changed, 81 insertions, 54 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 0acea7cf714..0bffb6a4253 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -541,39 +541,22 @@ ieee80211_associate(struct ieee80211_work *wk)
541static enum work_action __must_check 541static enum work_action __must_check
542ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) 542ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
543{ 543{
544 struct ieee80211_sub_if_data *sdata = wk->sdata;
545 struct ieee80211_local *local = sdata->local;
546
547 /* 544 /*
548 * First time we run, do nothing -- the generic code will 545 * First time we run, do nothing -- the generic code will
549 * have switched to the right channel etc. 546 * have switched to the right channel etc.
550 */ 547 */
551 if (wk->timeout != wk->remain.timeout) { 548 if (!wk->remain.started) {
552 wk->timeout = wk->remain.timeout; 549 wk->remain.started = true;
553 return WORK_ACT_NONE; 550 wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
554 }
555 551
556 /* 552 cfg80211_ready_on_channel(wk->sdata->dev, (u64)wk, wk->chan,
557 * We are done serving the remain-on-channel command; kill the work 553 wk->chan_type, wk->remain.duration,
558 * item to allow idle state to be entered again. In addition, clear the 554 GFP_KERNEL);
559 * temporary channel information to allow operational channel to be
560 * used.
561 */
562 list_del(&wk->list);
563 free_work(wk);
564 555
565 if (local->tmp_channel) { 556 return WORK_ACT_NONE;
566 cfg80211_remain_on_channel_expired(sdata->dev, (u64)wk,
567 local->tmp_channel,
568 local->tmp_channel_type,
569 GFP_KERNEL);
570
571 local->tmp_channel = NULL;
572 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
573 ieee80211_offchannel_return(local, true);
574 } 557 }
575 558
576 return WORK_ACT_NONE; 559 return WORK_ACT_TIMEOUT;
577} 560}
578 561
579static void ieee80211_auth_challenge(struct ieee80211_work *wk, 562static void ieee80211_auth_challenge(struct ieee80211_work *wk,
@@ -799,7 +782,7 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
799 break; 782 break;
800 case WORK_DONE_REQUEUE: 783 case WORK_DONE_REQUEUE:
801 synchronize_rcu(); 784 synchronize_rcu();
802 wk->timeout = jiffies; /* run again directly */ 785 wk->started = false; /* restart */
803 mutex_lock(&local->work_mtx); 786 mutex_lock(&local->work_mtx);
804 list_add_tail(&wk->list, &local->work_list); 787 list_add_tail(&wk->list, &local->work_list);
805 mutex_unlock(&local->work_mtx); 788 mutex_unlock(&local->work_mtx);
@@ -827,6 +810,7 @@ static void ieee80211_work_work(struct work_struct *work)
827 struct ieee80211_work *wk, *tmp; 810 struct ieee80211_work *wk, *tmp;
828 LIST_HEAD(free_work); 811 LIST_HEAD(free_work);
829 enum work_action rma; 812 enum work_action rma;
813 bool remain_off_channel = false;
830 814
831 if (local->scanning) 815 if (local->scanning)
832 return; 816 return;
@@ -847,6 +831,34 @@ static void ieee80211_work_work(struct work_struct *work)
847 mutex_lock(&local->work_mtx); 831 mutex_lock(&local->work_mtx);
848 832
849 list_for_each_entry_safe(wk, tmp, &local->work_list, list) { 833 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
834 /* mark work as started if it's on the current off-channel */
835 if (!wk->started && local->tmp_channel &&
836 wk->chan == local->tmp_channel &&
837 wk->chan_type == local->tmp_channel_type) {
838 wk->started = true;
839 }
840
841 if (!wk->started && !local->tmp_channel) {
842 /*
843 * TODO: could optimize this by leaving the
844 * station vifs in awake mode if they
845 * happen to be on the same channel as
846 * the requested channel
847 */
848 ieee80211_offchannel_stop_beaconing(local);
849 ieee80211_offchannel_stop_station(local);
850
851 local->tmp_channel = wk->chan;
852 local->tmp_channel_type = wk->chan_type;
853 ieee80211_hw_config(local, 0);
854 wk->started = true;
855 wk->timeout = jiffies;
856 }
857
858 /* don't try to work with items that aren't started */
859 if (!wk->started)
860 continue;
861
850 if (time_is_after_jiffies(wk->timeout)) { 862 if (time_is_after_jiffies(wk->timeout)) {
851 /* 863 /*
852 * This work item isn't supposed to be worked on 864 * This work item isn't supposed to be worked on
@@ -881,7 +893,8 @@ static void ieee80211_work_work(struct work_struct *work)
881 893
882 switch (rma) { 894 switch (rma) {
883 case WORK_ACT_NONE: 895 case WORK_ACT_NONE:
884 /* no action required */ 896 /* might have changed the timeout */
897 run_again(local, wk->timeout);
885 break; 898 break;
886 case WORK_ACT_TIMEOUT: 899 case WORK_ACT_TIMEOUT:
887 list_del_rcu(&wk->list); 900 list_del_rcu(&wk->list);
@@ -893,6 +906,24 @@ static void ieee80211_work_work(struct work_struct *work)
893 } 906 }
894 } 907 }
895 908
909 list_for_each_entry(wk, &local->work_list, list) {
910 if (!wk->started)
911 continue;
912 if (wk->chan != local->tmp_channel)
913 continue;
914 if (wk->chan_type != local->tmp_channel_type)
915 continue;
916 remain_off_channel = true;
917 }
918
919 if (!remain_off_channel && local->tmp_channel) {
920 local->tmp_channel = NULL;
921 ieee80211_hw_config(local, 0);
922 ieee80211_offchannel_return(local, true);
923 /* give connection some time to breathe */
924 run_again(local, jiffies + HZ/2);
925 }
926
896 if (list_empty(&local->work_list) && local->scan_req) 927 if (list_empty(&local->work_list) && local->scan_req)
897 ieee80211_queue_delayed_work(&local->hw, 928 ieee80211_queue_delayed_work(&local->hw,
898 &local->scan_work, 929 &local->scan_work,
@@ -900,6 +931,8 @@ static void ieee80211_work_work(struct work_struct *work)
900 931
901 mutex_unlock(&local->work_mtx); 932 mutex_unlock(&local->work_mtx);
902 933
934 ieee80211_recalc_idle(local);
935
903 list_for_each_entry_safe(wk, tmp, &free_work, list) { 936 list_for_each_entry_safe(wk, tmp, &free_work, list) {
904 wk->done(wk, NULL); 937 wk->done(wk, NULL);
905 list_del(&wk->list); 938 list_del(&wk->list);
@@ -920,7 +953,7 @@ void ieee80211_add_work(struct ieee80211_work *wk)
920 if (WARN_ON(!wk->done)) 953 if (WARN_ON(!wk->done))
921 return; 954 return;
922 955
923 wk->timeout = jiffies; 956 wk->started = false;
924 957
925 local = wk->sdata->local; 958 local = wk->sdata->local;
926 mutex_lock(&local->work_mtx); 959 mutex_lock(&local->work_mtx);
@@ -950,6 +983,8 @@ void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
950 if (wk->sdata != sdata) 983 if (wk->sdata != sdata)
951 continue; 984 continue;
952 wk->type = IEEE80211_WORK_ABORT; 985 wk->type = IEEE80211_WORK_ABORT;
986 wk->started = true;
987 wk->timeout = jiffies;
953 } 988 }
954 mutex_unlock(&local->work_mtx); 989 mutex_unlock(&local->work_mtx);
955 990
@@ -1004,12 +1039,24 @@ ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
1004 return RX_CONTINUE; 1039 return RX_CONTINUE;
1005} 1040}
1006 1041
1042static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
1043 struct sk_buff *skb)
1044{
1045 /*
1046 * We are done serving the remain-on-channel command.
1047 */
1048 cfg80211_remain_on_channel_expired(wk->sdata->dev, (u64)wk,
1049 wk->chan, wk->chan_type,
1050 GFP_KERNEL);
1051
1052 return WORK_DONE_DESTROY;
1053}
1054
1007int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, 1055int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1008 struct ieee80211_channel *chan, 1056 struct ieee80211_channel *chan,
1009 enum nl80211_channel_type channel_type, 1057 enum nl80211_channel_type channel_type,
1010 unsigned int duration, u64 *cookie) 1058 unsigned int duration, u64 *cookie)
1011{ 1059{
1012 struct ieee80211_local *local = sdata->local;
1013 struct ieee80211_work *wk; 1060 struct ieee80211_work *wk;
1014 1061
1015 wk = kzalloc(sizeof(*wk), GFP_KERNEL); 1062 wk = kzalloc(sizeof(*wk), GFP_KERNEL);
@@ -1018,28 +1065,16 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1018 1065
1019 wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; 1066 wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
1020 wk->chan = chan; 1067 wk->chan = chan;
1068 wk->chan_type = channel_type;
1021 wk->sdata = sdata; 1069 wk->sdata = sdata;
1070 wk->done = ieee80211_remain_done;
1022 1071
1023 wk->remain.timeout = jiffies + msecs_to_jiffies(duration); 1072 wk->remain.duration = duration;
1024 1073
1025 *cookie = (u64)wk; 1074 *cookie = (u64)wk;
1026 1075
1027 ieee80211_add_work(wk); 1076 ieee80211_add_work(wk);
1028 1077
1029 /*
1030 * TODO: could optimize this by leaving the station vifs in awake mode
1031 * if they happen to be on the same channel as the requested channel
1032 */
1033 ieee80211_offchannel_stop_beaconing(local);
1034 ieee80211_offchannel_stop_station(local);
1035
1036 sdata->local->tmp_channel = chan;
1037 sdata->local->tmp_channel_type = channel_type;
1038 ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
1039
1040 cfg80211_ready_on_channel(sdata->dev, (u64)wk, chan, channel_type,
1041 duration, GFP_KERNEL);
1042
1043 return 0; 1078 return 0;
1044} 1079}
1045 1080
@@ -1053,9 +1088,8 @@ int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1053 mutex_lock(&local->work_mtx); 1088 mutex_lock(&local->work_mtx);
1054 list_for_each_entry_safe(wk, tmp, &local->work_list, list) { 1089 list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
1055 if ((u64)wk == cookie) { 1090 if ((u64)wk == cookie) {
1091 wk->timeout = jiffies;
1056 found = true; 1092 found = true;
1057 list_del(&wk->list);
1058 free_work(wk);
1059 break; 1093 break;
1060 } 1094 }
1061 } 1095 }
@@ -1064,14 +1098,7 @@ int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
1064 if (!found) 1098 if (!found)
1065 return -ENOENT; 1099 return -ENOENT;
1066 1100
1067 if (sdata->local->tmp_channel) { 1101 ieee80211_queue_work(&local->hw, &local->work_work);
1068 sdata->local->tmp_channel = NULL;
1069 ieee80211_hw_config(sdata->local,
1070 IEEE80211_CONF_CHANGE_CHANNEL);
1071 ieee80211_offchannel_return(sdata->local, true);
1072 }
1073
1074 ieee80211_recalc_idle(local);
1075 1102
1076 return 0; 1103 return 0;
1077} 1104}