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 | |
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')
-rw-r--r-- | net/mac80211/chan.c | 69 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 5 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 99 |
3 files changed, 69 insertions, 104 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index ea4db2f0db87..c3fd4d275bf4 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
@@ -819,70 +819,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
819 | return ret; | 819 | return ret; |
820 | } | 820 | } |
821 | 821 | ||
822 | static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | ||
823 | struct ieee80211_chanctx *ctx, | ||
824 | u32 *changed) | ||
825 | { | ||
826 | struct ieee80211_local *local = sdata->local; | ||
827 | const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; | ||
828 | u32 chanctx_changed = 0; | ||
829 | |||
830 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | ||
831 | IEEE80211_CHAN_DISABLED)) | ||
832 | return -EINVAL; | ||
833 | |||
834 | if (ieee80211_chanctx_refcount(local, ctx) != 1) | ||
835 | return -EINVAL; | ||
836 | |||
837 | if (sdata->vif.bss_conf.chandef.width != chandef->width) { | ||
838 | chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; | ||
839 | *changed |= BSS_CHANGED_BANDWIDTH; | ||
840 | } | ||
841 | |||
842 | sdata->vif.bss_conf.chandef = *chandef; | ||
843 | ctx->conf.def = *chandef; | ||
844 | |||
845 | chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; | ||
846 | drv_change_chanctx(local, ctx, chanctx_changed); | ||
847 | |||
848 | ieee80211_recalc_chanctx_chantype(local, ctx); | ||
849 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
850 | ieee80211_recalc_radar_chanctx(local, ctx); | ||
851 | ieee80211_recalc_chanctx_min_def(local, ctx); | ||
852 | |||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | ||
857 | u32 *changed) | ||
858 | { | ||
859 | struct ieee80211_local *local = sdata->local; | ||
860 | struct ieee80211_chanctx_conf *conf; | ||
861 | struct ieee80211_chanctx *ctx; | ||
862 | int ret; | ||
863 | |||
864 | lockdep_assert_held(&local->mtx); | ||
865 | |||
866 | /* should never be called if not performing a channel switch. */ | ||
867 | if (WARN_ON(!sdata->vif.csa_active)) | ||
868 | return -EINVAL; | ||
869 | |||
870 | mutex_lock(&local->chanctx_mtx); | ||
871 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
872 | lockdep_is_held(&local->chanctx_mtx)); | ||
873 | if (!conf) { | ||
874 | ret = -EINVAL; | ||
875 | goto out; | ||
876 | } | ||
877 | |||
878 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | ||
879 | |||
880 | ret = __ieee80211_vif_change_channel(sdata, ctx, changed); | ||
881 | out: | ||
882 | mutex_unlock(&local->chanctx_mtx); | ||
883 | return ret; | ||
884 | } | ||
885 | |||
886 | static void | 822 | static void |
887 | __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | 823 | __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, |
888 | bool clear) | 824 | bool clear) |
@@ -1066,8 +1002,11 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) | |||
1066 | ieee80211_queue_work(&sdata->local->hw, | 1002 | ieee80211_queue_work(&sdata->local->hw, |
1067 | &sdata->csa_finalize_work); | 1003 | &sdata->csa_finalize_work); |
1068 | break; | 1004 | break; |
1069 | case NL80211_IFTYPE_UNSPECIFIED: | ||
1070 | case NL80211_IFTYPE_STATION: | 1005 | case NL80211_IFTYPE_STATION: |
1006 | ieee80211_queue_work(&sdata->local->hw, | ||
1007 | &sdata->u.mgd.chswitch_work); | ||
1008 | break; | ||
1009 | case NL80211_IFTYPE_UNSPECIFIED: | ||
1071 | case NL80211_IFTYPE_AP_VLAN: | 1010 | case NL80211_IFTYPE_AP_VLAN: |
1072 | case NL80211_IFTYPE_WDS: | 1011 | case NL80211_IFTYPE_WDS: |
1073 | case NL80211_IFTYPE_MONITOR: | 1012 | case NL80211_IFTYPE_MONITOR: |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c26955f6c14d..9e025e1184cc 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -788,7 +788,6 @@ struct ieee80211_sub_if_data { | |||
788 | struct mac80211_qos_map __rcu *qos_map; | 788 | struct mac80211_qos_map __rcu *qos_map; |
789 | 789 | ||
790 | struct work_struct csa_finalize_work; | 790 | struct work_struct csa_finalize_work; |
791 | bool csa_radar_required; | ||
792 | bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ | 791 | bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ |
793 | struct cfg80211_chan_def csa_chandef; | 792 | struct cfg80211_chan_def csa_chandef; |
794 | 793 | ||
@@ -1850,10 +1849,6 @@ int __must_check | |||
1850 | ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | 1849 | ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, |
1851 | const struct cfg80211_chan_def *chandef, | 1850 | const struct cfg80211_chan_def *chandef, |
1852 | u32 *changed); | 1851 | u32 *changed); |
1853 | /* NOTE: only use ieee80211_vif_change_channel() for channel switch */ | ||
1854 | int __must_check | ||
1855 | ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | ||
1856 | u32 *changed); | ||
1857 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); | 1852 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); |
1858 | void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); | 1853 | void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); |
1859 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | 1854 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, |
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) |