diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2014-06-25 06:35:09 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-06-25 12:06:20 -0400 |
commit | 4c3ebc56d7561526524ec62c61aa3e2040b71f6e (patch) | |
tree | 9fe45d875f637b6ba0a93829e6ffa492cba2a35d /net/mac80211/mlme.c | |
parent | 03078de4f928ffcbe629a914dea8bdf66a9d6a48 (diff) |
mac80211: use chanctx reservation for STA CSA
Channel switch finalization is now 2-step. First
step is when driver calls chswitch_done(), the
other is when reservation is actually finalized
(which be defered for in-place reservation).
It is now safe to call ieee80211_chswitch_done()
more than once.
Also remove the ieee80211_vif_change_channel()
because it is no longer used.
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 99 |
1 files changed, 65 insertions, 34 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index eccc8492a59c..931330bbe00c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -940,52 +940,70 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
940 | container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); | 940 | container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); |
941 | struct ieee80211_local *local = sdata->local; | 941 | struct ieee80211_local *local = sdata->local; |
942 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 942 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
943 | u32 changed = 0; | ||
944 | int ret; | 943 | int ret; |
945 | 944 | ||
946 | if (!ieee80211_sdata_running(sdata)) | 945 | if (!ieee80211_sdata_running(sdata)) |
947 | return; | 946 | return; |
948 | 947 | ||
949 | sdata_lock(sdata); | 948 | sdata_lock(sdata); |
949 | mutex_lock(&local->mtx); | ||
950 | mutex_lock(&local->chanctx_mtx); | ||
951 | |||
950 | if (!ifmgd->associated) | 952 | if (!ifmgd->associated) |
951 | goto out; | 953 | goto out; |
952 | 954 | ||
953 | mutex_lock(&local->mtx); | 955 | if (!sdata->vif.csa_active) |
954 | ret = ieee80211_vif_change_channel(sdata, &changed); | 956 | goto out; |
955 | mutex_unlock(&local->mtx); | 957 | |
956 | if (ret) { | 958 | /* |
959 | * using reservation isn't immediate as it may be deferred until later | ||
960 | * with multi-vif. once reservation is complete it will re-schedule the | ||
961 | * work with no reserved_chanctx so verify chandef to check if it | ||
962 | * completed successfully | ||
963 | */ | ||
964 | |||
965 | if (sdata->reserved_chanctx) { | ||
966 | /* | ||
967 | * with multi-vif csa driver may call ieee80211_csa_finish() | ||
968 | * many times while waiting for other interfaces to use their | ||
969 | * reservations | ||
970 | */ | ||
971 | if (sdata->reserved_ready) | ||
972 | goto out; | ||
973 | |||
974 | ret = ieee80211_vif_use_reserved_context(sdata); | ||
975 | if (ret) { | ||
976 | sdata_info(sdata, | ||
977 | "failed to use reserved channel context, disconnecting (err=%d)\n", | ||
978 | ret); | ||
979 | ieee80211_queue_work(&sdata->local->hw, | ||
980 | &ifmgd->csa_connection_drop_work); | ||
981 | goto out; | ||
982 | } | ||
983 | |||
984 | goto out; | ||
985 | } | ||
986 | |||
987 | if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, | ||
988 | &sdata->csa_chandef)) { | ||
957 | sdata_info(sdata, | 989 | sdata_info(sdata, |
958 | "vif channel switch failed, disconnecting\n"); | 990 | "failed to finalize channel switch, disconnecting\n"); |
959 | ieee80211_queue_work(&sdata->local->hw, | 991 | ieee80211_queue_work(&sdata->local->hw, |
960 | &ifmgd->csa_connection_drop_work); | 992 | &ifmgd->csa_connection_drop_work); |
961 | goto out; | 993 | goto out; |
962 | } | 994 | } |
963 | 995 | ||
964 | if (!local->use_chanctx) { | ||
965 | local->_oper_chandef = sdata->csa_chandef; | ||
966 | /* Call "hw_config" only if doing sw channel switch. | ||
967 | * Otherwise update the channel directly | ||
968 | */ | ||
969 | if (!local->ops->channel_switch) | ||
970 | ieee80211_hw_config(local, 0); | ||
971 | else | ||
972 | local->hw.conf.chandef = local->_oper_chandef; | ||
973 | } | ||
974 | |||
975 | /* XXX: shouldn't really modify cfg80211-owned data! */ | 996 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
976 | ifmgd->associated->channel = sdata->csa_chandef.chan; | 997 | ifmgd->associated->channel = sdata->csa_chandef.chan; |
977 | 998 | ||
978 | ieee80211_bss_info_change_notify(sdata, changed); | ||
979 | |||
980 | mutex_lock(&local->mtx); | ||
981 | sdata->vif.csa_active = false; | 999 | sdata->vif.csa_active = false; |
1000 | |||
982 | /* XXX: wait for a beacon first? */ | 1001 | /* XXX: wait for a beacon first? */ |
983 | if (sdata->csa_block_tx) { | 1002 | if (sdata->csa_block_tx) { |
984 | ieee80211_wake_vif_queues(local, sdata, | 1003 | ieee80211_wake_vif_queues(local, sdata, |
985 | IEEE80211_QUEUE_STOP_REASON_CSA); | 1004 | IEEE80211_QUEUE_STOP_REASON_CSA); |
986 | sdata->csa_block_tx = false; | 1005 | sdata->csa_block_tx = false; |
987 | } | 1006 | } |
988 | mutex_unlock(&local->mtx); | ||
989 | 1007 | ||
990 | ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; | 1008 | ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; |
991 | 1009 | ||
@@ -993,6 +1011,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
993 | ieee80211_sta_reset_conn_monitor(sdata); | 1011 | ieee80211_sta_reset_conn_monitor(sdata); |
994 | 1012 | ||
995 | out: | 1013 | out: |
1014 | mutex_unlock(&local->chanctx_mtx); | ||
1015 | mutex_unlock(&local->mtx); | ||
996 | sdata_unlock(sdata); | 1016 | sdata_unlock(sdata); |
997 | } | 1017 | } |
998 | 1018 | ||
@@ -1029,6 +1049,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1029 | struct ieee80211_local *local = sdata->local; | 1049 | struct ieee80211_local *local = sdata->local; |
1030 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1050 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1031 | struct cfg80211_bss *cbss = ifmgd->associated; | 1051 | struct cfg80211_bss *cbss = ifmgd->associated; |
1052 | struct ieee80211_chanctx_conf *conf; | ||
1032 | struct ieee80211_chanctx *chanctx; | 1053 | struct ieee80211_chanctx *chanctx; |
1033 | enum ieee80211_band current_band; | 1054 | enum ieee80211_band current_band; |
1034 | struct ieee80211_csa_ie csa_ie; | 1055 | struct ieee80211_csa_ie csa_ie; |
@@ -1072,7 +1093,22 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1072 | 1093 | ||
1073 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | 1094 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; |
1074 | 1095 | ||
1096 | mutex_lock(&local->mtx); | ||
1075 | mutex_lock(&local->chanctx_mtx); | 1097 | mutex_lock(&local->chanctx_mtx); |
1098 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
1099 | lockdep_is_held(&local->chanctx_mtx)); | ||
1100 | if (!conf) { | ||
1101 | sdata_info(sdata, | ||
1102 | "no channel context assigned to vif?, disconnecting\n"); | ||
1103 | ieee80211_queue_work(&local->hw, | ||
1104 | &ifmgd->csa_connection_drop_work); | ||
1105 | mutex_unlock(&local->chanctx_mtx); | ||
1106 | mutex_unlock(&local->mtx); | ||
1107 | return; | ||
1108 | } | ||
1109 | |||
1110 | chanctx = container_of(conf, struct ieee80211_chanctx, conf); | ||
1111 | |||
1076 | if (local->use_chanctx) { | 1112 | if (local->use_chanctx) { |
1077 | u32 num_chanctx = 0; | 1113 | u32 num_chanctx = 0; |
1078 | list_for_each_entry(chanctx, &local->chanctx_list, list) | 1114 | list_for_each_entry(chanctx, &local->chanctx_list, list) |
@@ -1085,32 +1121,27 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1085 | ieee80211_queue_work(&local->hw, | 1121 | ieee80211_queue_work(&local->hw, |
1086 | &ifmgd->csa_connection_drop_work); | 1122 | &ifmgd->csa_connection_drop_work); |
1087 | mutex_unlock(&local->chanctx_mtx); | 1123 | mutex_unlock(&local->chanctx_mtx); |
1124 | mutex_unlock(&local->mtx); | ||
1088 | return; | 1125 | return; |
1089 | } | 1126 | } |
1090 | } | 1127 | } |
1091 | 1128 | ||
1092 | if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { | 1129 | res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, |
1093 | ieee80211_queue_work(&local->hw, | 1130 | chanctx->mode, false); |
1094 | &ifmgd->csa_connection_drop_work); | 1131 | if (res) { |
1095 | mutex_unlock(&local->chanctx_mtx); | ||
1096 | return; | ||
1097 | } | ||
1098 | chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), | ||
1099 | struct ieee80211_chanctx, conf); | ||
1100 | if (ieee80211_chanctx_refcount(local, chanctx) > 1) { | ||
1101 | sdata_info(sdata, | 1132 | sdata_info(sdata, |
1102 | "channel switch with multiple interfaces on the same channel, disconnecting\n"); | 1133 | "failed to reserve channel context for channel switch, disconnecting (err=%d)\n", |
1134 | res); | ||
1103 | ieee80211_queue_work(&local->hw, | 1135 | ieee80211_queue_work(&local->hw, |
1104 | &ifmgd->csa_connection_drop_work); | 1136 | &ifmgd->csa_connection_drop_work); |
1105 | mutex_unlock(&local->chanctx_mtx); | 1137 | mutex_unlock(&local->chanctx_mtx); |
1138 | mutex_unlock(&local->mtx); | ||
1106 | return; | 1139 | return; |
1107 | } | 1140 | } |
1108 | mutex_unlock(&local->chanctx_mtx); | 1141 | mutex_unlock(&local->chanctx_mtx); |
1109 | 1142 | ||
1110 | sdata->csa_chandef = csa_ie.chandef; | ||
1111 | |||
1112 | mutex_lock(&local->mtx); | ||
1113 | sdata->vif.csa_active = true; | 1143 | sdata->vif.csa_active = true; |
1144 | sdata->csa_chandef = csa_ie.chandef; | ||
1114 | sdata->csa_block_tx = csa_ie.mode; | 1145 | sdata->csa_block_tx = csa_ie.mode; |
1115 | 1146 | ||
1116 | if (sdata->csa_block_tx) | 1147 | if (sdata->csa_block_tx) |