aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2014-06-25 06:35:06 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-06-25 12:06:20 -0400
commit5bcae31d9cb1ebfad3ad5a3eea04c8cdc329a04f (patch)
tree838badeb7af332aae425df02eb541851c309fcf2
parent633e27132625a0692440c4db58b901fb3cb67c55 (diff)
mac80211: implement multi-vif in-place reservations
Multi-vif in-place reservations happen when it is impossible to allocate more channel contexts as indicated by interface combinations. Such reservations are not finalized until all assigned interfaces are ready. This still doesn't handle all possible cases (i.e. degradation of number of channels) properly. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--include/net/mac80211.h8
-rw-r--r--net/mac80211/chan.c765
-rw-r--r--net/mac80211/ieee80211_i.h26
-rw-r--r--net/mac80211/util.c9
4 files changed, 716 insertions, 92 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 545d2fa179c4..9ce5cb17ed82 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1606,12 +1606,6 @@ struct ieee80211_tx_control {
1606 * is not enabled the default action is to disconnect when getting the 1606 * is not enabled the default action is to disconnect when getting the
1607 * CSA frame. 1607 * CSA frame.
1608 * 1608 *
1609 * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
1610 * channel context on-the-fly. This is needed for channel switch
1611 * on single-channel hardware. It can also be used as an
1612 * optimization in certain channel switch cases with
1613 * multi-channel.
1614 *
1615 * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands 1609 * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
1616 * in one command, mac80211 doesn't have to run separate scans per band. 1610 * in one command, mac80211 doesn't have to run separate scans per band.
1617 */ 1611 */
@@ -1645,7 +1639,7 @@ enum ieee80211_hw_flags {
1645 IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, 1639 IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
1646 IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, 1640 IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
1647 IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, 1641 IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
1648 IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, 1642 /* bit 29 unused */
1649 IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, 1643 IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30,
1650}; 1644};
1651 1645
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index a310e33972de..0e4302bb5b34 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
63 return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); 63 return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
64} 64}
65 65
66static struct ieee80211_chanctx *
67ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
68{
69 struct ieee80211_local *local = sdata->local;
70 struct ieee80211_chanctx_conf *conf;
71
72 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
73 lockdep_is_held(&local->chanctx_mtx));
74 if (!conf)
75 return NULL;
76
77 return container_of(conf, struct ieee80211_chanctx, conf);
78}
79
66static const struct cfg80211_chan_def * 80static const struct cfg80211_chan_def *
67ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, 81ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
68 struct ieee80211_chanctx *ctx, 82 struct ieee80211_chanctx *ctx,
@@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
160 return NULL; 174 return NULL;
161 175
162 list_for_each_entry(ctx, &local->chanctx_list, list) { 176 list_for_each_entry(ctx, &local->chanctx_list, list) {
177 if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
178 continue;
179
163 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 180 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
164 continue; 181 continue;
165 182
@@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
347 list_for_each_entry(ctx, &local->chanctx_list, list) { 364 list_for_each_entry(ctx, &local->chanctx_list, list) {
348 const struct cfg80211_chan_def *compat; 365 const struct cfg80211_chan_def *compat;
349 366
367 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
368 continue;
369
350 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 370 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
351 continue; 371 continue;
352 372
@@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
622 struct ieee80211_local *local = sdata->local; 642 struct ieee80211_local *local = sdata->local;
623 struct ieee80211_chanctx_conf *conf; 643 struct ieee80211_chanctx_conf *conf;
624 struct ieee80211_chanctx *ctx; 644 struct ieee80211_chanctx *ctx;
645 bool use_reserved_switch = false;
625 646
626 lockdep_assert_held(&local->chanctx_mtx); 647 lockdep_assert_held(&local->chanctx_mtx);
627 648
@@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
632 653
633 ctx = container_of(conf, struct ieee80211_chanctx, conf); 654 ctx = container_of(conf, struct ieee80211_chanctx, conf);
634 655
635 if (sdata->reserved_chanctx) 656 if (sdata->reserved_chanctx) {
657 if (sdata->reserved_chanctx->replace_state ==
658 IEEE80211_CHANCTX_REPLACES_OTHER &&
659 ieee80211_chanctx_num_reserved(local,
660 sdata->reserved_chanctx) > 1)
661 use_reserved_switch = true;
662
636 ieee80211_vif_unreserve_chanctx(sdata); 663 ieee80211_vif_unreserve_chanctx(sdata);
664 }
637 665
638 ieee80211_assign_vif_chanctx(sdata, NULL); 666 ieee80211_assign_vif_chanctx(sdata, NULL);
639 if (ieee80211_chanctx_refcount(local, ctx) == 0) 667 if (ieee80211_chanctx_refcount(local, ctx) == 0)
640 ieee80211_free_chanctx(local, ctx); 668 ieee80211_free_chanctx(local, ctx);
669
670 /* Unreserving may ready an in-place reservation. */
671 if (use_reserved_switch)
672 ieee80211_vif_use_reserved_switch(local);
641} 673}
642 674
643void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 675void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
@@ -905,8 +937,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
905 list_del(&sdata->reserved_chanctx_list); 937 list_del(&sdata->reserved_chanctx_list);
906 sdata->reserved_chanctx = NULL; 938 sdata->reserved_chanctx = NULL;
907 939
908 if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) 940 if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
909 ieee80211_free_chanctx(sdata->local, ctx); 941 if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
942 if (WARN_ON(!ctx->replace_ctx))
943 return -EINVAL;
944
945 WARN_ON(ctx->replace_ctx->replace_state !=
946 IEEE80211_CHANCTX_WILL_BE_REPLACED);
947 WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
948
949 ctx->replace_ctx->replace_ctx = NULL;
950 ctx->replace_ctx->replace_state =
951 IEEE80211_CHANCTX_REPLACE_NONE;
952
953 list_del_rcu(&ctx->list);
954 kfree_rcu(ctx, rcu_head);
955 } else {
956 ieee80211_free_chanctx(sdata->local, ctx);
957 }
958 }
910 959
911 return 0; 960 return 0;
912} 961}
@@ -917,40 +966,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
917 bool radar_required) 966 bool radar_required)
918{ 967{
919 struct ieee80211_local *local = sdata->local; 968 struct ieee80211_local *local = sdata->local;
920 struct ieee80211_chanctx_conf *conf; 969 struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
921 struct ieee80211_chanctx *new_ctx, *curr_ctx;
922 int ret = 0;
923 970
924 mutex_lock(&local->chanctx_mtx); 971 lockdep_assert_held(&local->chanctx_mtx);
925
926 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
927 lockdep_is_held(&local->chanctx_mtx));
928 if (!conf) {
929 ret = -EINVAL;
930 goto out;
931 }
932 972
933 curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 973 curr_ctx = ieee80211_vif_get_chanctx(sdata);
974 if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
975 return -ENOTSUPP;
934 976
935 new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 977 new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
936 if (!new_ctx) { 978 if (!new_ctx) {
937 if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && 979 if (ieee80211_can_create_new_chanctx(local)) {
938 (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
939 /* if we're the only users of the chanctx and
940 * the driver supports changing a running
941 * context, reserve our current context
942 */
943 new_ctx = curr_ctx;
944 } else if (ieee80211_can_create_new_chanctx(local)) {
945 /* create a new context and reserve it */
946 new_ctx = ieee80211_new_chanctx(local, chandef, mode); 980 new_ctx = ieee80211_new_chanctx(local, chandef, mode);
947 if (IS_ERR(new_ctx)) { 981 if (IS_ERR(new_ctx))
948 ret = PTR_ERR(new_ctx); 982 return PTR_ERR(new_ctx);
949 goto out;
950 }
951 } else { 983 } else {
952 ret = -EBUSY; 984 if (!curr_ctx ||
953 goto out; 985 (curr_ctx->replace_state ==
986 IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
987 !list_empty(&curr_ctx->reserved_vifs)) {
988 /*
989 * Another vif already requested this context
990 * for a reservation. Find another one hoping
991 * all vifs assigned to it will also switch
992 * soon enough.
993 *
994 * TODO: This needs a little more work as some
995 * cases (more than 2 chanctx capable devices)
996 * may fail which could otherwise succeed
997 * provided some channel context juggling was
998 * performed.
999 *
1000 * Consider ctx1..3, vif1..6, each ctx has 2
1001 * vifs. vif1 and vif2 from ctx1 request new
1002 * different chandefs starting 2 in-place
1003 * reserations with ctx4 and ctx5 replacing
1004 * ctx1 and ctx2 respectively. Next vif5 and
1005 * vif6 from ctx3 reserve ctx4. If vif3 and
1006 * vif4 remain on ctx2 as they are then this
1007 * fails unless `replace_ctx` from ctx5 is
1008 * replaced with ctx3.
1009 */
1010 list_for_each_entry(ctx, &local->chanctx_list,
1011 list) {
1012 if (ctx->replace_state !=
1013 IEEE80211_CHANCTX_REPLACE_NONE)
1014 continue;
1015
1016 if (!list_empty(&ctx->reserved_vifs))
1017 continue;
1018
1019 curr_ctx = ctx;
1020 break;
1021 }
1022 }
1023
1024 /*
1025 * If that's true then all available contexts already
1026 * have reservations and cannot be used.
1027 */
1028 if (!curr_ctx ||
1029 (curr_ctx->replace_state ==
1030 IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1031 !list_empty(&curr_ctx->reserved_vifs))
1032 return -EBUSY;
1033
1034 new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
1035 if (!new_ctx)
1036 return -ENOMEM;
1037
1038 new_ctx->replace_ctx = curr_ctx;
1039 new_ctx->replace_state =
1040 IEEE80211_CHANCTX_REPLACES_OTHER;
1041
1042 curr_ctx->replace_ctx = new_ctx;
1043 curr_ctx->replace_state =
1044 IEEE80211_CHANCTX_WILL_BE_REPLACED;
1045
1046 list_add_rcu(&new_ctx->list, &local->chanctx_list);
954 } 1047 }
955 } 1048 }
956 1049
@@ -958,82 +1051,567 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
958 sdata->reserved_chanctx = new_ctx; 1051 sdata->reserved_chanctx = new_ctx;
959 sdata->reserved_chandef = *chandef; 1052 sdata->reserved_chandef = *chandef;
960 sdata->reserved_radar_required = radar_required; 1053 sdata->reserved_radar_required = radar_required;
961out: 1054 sdata->reserved_ready = false;
962 mutex_unlock(&local->chanctx_mtx); 1055
963 return ret; 1056 return 0;
964} 1057}
965 1058
966int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, 1059static int
967 u32 *changed) 1060ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
968{ 1061{
969 struct ieee80211_local *local = sdata->local; 1062 struct ieee80211_local *local = sdata->local;
970 struct ieee80211_chanctx *ctx; 1063 struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
971 struct ieee80211_chanctx *old_ctx; 1064 struct ieee80211_chanctx *old_ctx, *new_ctx;
972 struct ieee80211_chanctx_conf *conf; 1065 const struct cfg80211_chan_def *chandef;
973 int ret; 1066 u32 changed = 0;
974 u32 tmp_changed = *changed; 1067 int err;
975
976 /* TODO: need to recheck if the chandef is usable etc.? */
977 1068
978 lockdep_assert_held(&local->mtx); 1069 lockdep_assert_held(&local->mtx);
1070 lockdep_assert_held(&local->chanctx_mtx);
979 1071
980 mutex_lock(&local->chanctx_mtx); 1072 new_ctx = sdata->reserved_chanctx;
1073 old_ctx = ieee80211_vif_get_chanctx(sdata);
981 1074
982 ctx = sdata->reserved_chanctx; 1075 if (WARN_ON(!sdata->reserved_ready))
983 if (WARN_ON(!ctx)) { 1076 return -EBUSY;
984 ret = -EINVAL;
985 goto out;
986 }
987 1077
988 conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1078 if (WARN_ON(!new_ctx))
989 lockdep_is_held(&local->chanctx_mtx)); 1079 return -EINVAL;
990 if (!conf) { 1080
991 ret = -EINVAL; 1081 if (WARN_ON(!old_ctx))
992 goto out; 1082 return -EINVAL;
1083
1084 if (WARN_ON(new_ctx->replace_state ==
1085 IEEE80211_CHANCTX_REPLACES_OTHER))
1086 return -EINVAL;
1087
1088 chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1089 &sdata->reserved_chandef);
1090 if (WARN_ON(!chandef))
1091 return -EINVAL;
1092
1093 vif_chsw[0].vif = &sdata->vif;
1094 vif_chsw[0].old_ctx = &old_ctx->conf;
1095 vif_chsw[0].new_ctx = &new_ctx->conf;
1096
1097 list_del(&sdata->reserved_chanctx_list);
1098 sdata->reserved_chanctx = NULL;
1099
1100 err = drv_switch_vif_chanctx(local, vif_chsw, 1,
1101 CHANCTX_SWMODE_REASSIGN_VIF);
1102 if (err) {
1103 if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1104 ieee80211_free_chanctx(local, new_ctx);
1105
1106 return err;
993 } 1107 }
994 1108
995 old_ctx = container_of(conf, struct ieee80211_chanctx, conf); 1109 list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
1110 rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
1111
1112 if (sdata->vif.type == NL80211_IFTYPE_AP)
1113 __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
1114
1115 if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
1116 ieee80211_free_chanctx(local, old_ctx);
996 1117
997 if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 1118 if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
998 tmp_changed |= BSS_CHANGED_BANDWIDTH; 1119 changed = BSS_CHANGED_BANDWIDTH;
999 1120
1000 sdata->vif.bss_conf.chandef = sdata->reserved_chandef; 1121 sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
1001 1122
1002 /* unref our reservation */ 1123 if (changed)
1003 sdata->reserved_chanctx = NULL; 1124 ieee80211_bss_info_change_notify(sdata, changed);
1004 sdata->radar_required = sdata->reserved_radar_required; 1125
1126 return err;
1127}
1128
1129static int
1130ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
1131{
1132 struct ieee80211_local *local = sdata->local;
1133 struct ieee80211_chanctx *old_ctx, *new_ctx;
1134 const struct cfg80211_chan_def *chandef;
1135 int err;
1136
1137 old_ctx = ieee80211_vif_get_chanctx(sdata);
1138 new_ctx = sdata->reserved_chanctx;
1139
1140 if (WARN_ON(!sdata->reserved_ready))
1141 return -EINVAL;
1142
1143 if (WARN_ON(old_ctx))
1144 return -EINVAL;
1145
1146 if (WARN_ON(!new_ctx))
1147 return -EINVAL;
1148
1149 if (WARN_ON(new_ctx->replace_state ==
1150 IEEE80211_CHANCTX_REPLACES_OTHER))
1151 return -EINVAL;
1152
1153 chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1154 &sdata->reserved_chandef);
1155 if (WARN_ON(!chandef))
1156 return -EINVAL;
1157
1005 list_del(&sdata->reserved_chanctx_list); 1158 list_del(&sdata->reserved_chanctx_list);
1159 sdata->reserved_chanctx = NULL;
1006 1160
1007 if (old_ctx == ctx) { 1161 err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
1008 /* This is our own context, just change it */ 1162 if (err) {
1009 ret = __ieee80211_vif_change_channel(sdata, old_ctx, 1163 if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1010 &tmp_changed); 1164 ieee80211_free_chanctx(local, new_ctx);
1011 if (ret) 1165
1012 goto out; 1166 goto out;
1013 } else { 1167 }
1014 ret = ieee80211_assign_vif_chanctx(sdata, ctx); 1168
1015 if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 1169out:
1016 ieee80211_free_chanctx(local, old_ctx); 1170 return err;
1017 if (ret) { 1171}
1018 /* if assign fails refcount stays the same */ 1172
1019 if (ieee80211_chanctx_refcount(local, ctx) == 0) 1173static bool
1020 ieee80211_free_chanctx(local, ctx); 1174ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
1175{
1176 struct ieee80211_chanctx *old_ctx, *new_ctx;
1177
1178 lockdep_assert_held(&sdata->local->chanctx_mtx);
1179
1180 new_ctx = sdata->reserved_chanctx;
1181 old_ctx = ieee80211_vif_get_chanctx(sdata);
1182
1183 if (!old_ctx)
1184 return false;
1185
1186 if (WARN_ON(!new_ctx))
1187 return false;
1188
1189 if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1190 return false;
1191
1192 if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1193 return false;
1194
1195 return true;
1196}
1197
1198static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
1199 struct ieee80211_chanctx *new_ctx)
1200{
1201 const struct cfg80211_chan_def *chandef;
1202
1203 lockdep_assert_held(&local->mtx);
1204 lockdep_assert_held(&local->chanctx_mtx);
1205
1206 chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
1207 if (WARN_ON(!chandef))
1208 return -EINVAL;
1209
1210 local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
1211 local->_oper_chandef = *chandef;
1212 ieee80211_hw_config(local, 0);
1213
1214 return 0;
1215}
1216
1217static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
1218 int n_vifs)
1219{
1220 struct ieee80211_vif_chanctx_switch *vif_chsw;
1221 struct ieee80211_sub_if_data *sdata;
1222 struct ieee80211_chanctx *ctx, *old_ctx;
1223 int i, err;
1224
1225 lockdep_assert_held(&local->mtx);
1226 lockdep_assert_held(&local->chanctx_mtx);
1227
1228 vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL);
1229 if (!vif_chsw)
1230 return -ENOMEM;
1231
1232 i = 0;
1233 list_for_each_entry(ctx, &local->chanctx_list, list) {
1234 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1235 continue;
1236
1237 if (WARN_ON(!ctx->replace_ctx)) {
1238 err = -EINVAL;
1021 goto out; 1239 goto out;
1022 } 1240 }
1023 1241
1024 if (sdata->vif.type == NL80211_IFTYPE_AP) 1242 list_for_each_entry(sdata, &ctx->reserved_vifs,
1025 __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 1243 reserved_chanctx_list) {
1244 if (!ieee80211_vif_has_in_place_reservation(
1245 sdata))
1246 continue;
1247
1248 old_ctx = ieee80211_vif_get_chanctx(sdata);
1249 vif_chsw[i].vif = &sdata->vif;
1250 vif_chsw[i].old_ctx = &old_ctx->conf;
1251 vif_chsw[i].new_ctx = &ctx->conf;
1252
1253 i++;
1254 }
1026 } 1255 }
1027 1256
1028 *changed = tmp_changed; 1257 err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
1258 CHANCTX_SWMODE_SWAP_CONTEXTS);
1029 1259
1030 ieee80211_recalc_chanctx_chantype(local, ctx);
1031 ieee80211_recalc_smps_chanctx(local, ctx);
1032 ieee80211_recalc_radar_chanctx(local, ctx);
1033 ieee80211_recalc_chanctx_min_def(local, ctx);
1034out: 1260out:
1035 mutex_unlock(&local->chanctx_mtx); 1261 kfree(vif_chsw);
1036 return ret; 1262 return err;
1263}
1264
1265static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
1266{
1267 struct ieee80211_chanctx *ctx;
1268 int err;
1269
1270 lockdep_assert_held(&local->mtx);
1271 lockdep_assert_held(&local->chanctx_mtx);
1272
1273 list_for_each_entry(ctx, &local->chanctx_list, list) {
1274 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1275 continue;
1276
1277 if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1278 continue;
1279
1280 ieee80211_del_chanctx(local, ctx->replace_ctx);
1281 err = ieee80211_add_chanctx(local, ctx);
1282 if (err)
1283 goto err;
1284 }
1285
1286 return 0;
1287
1288err:
1289 WARN_ON(ieee80211_add_chanctx(local, ctx));
1290 list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
1291 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1292 continue;
1293
1294 if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1295 continue;
1296
1297 ieee80211_del_chanctx(local, ctx);
1298 WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
1299 }
1300
1301 return err;
1302}
1303
1304int
1305ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
1306{
1307 struct ieee80211_sub_if_data *sdata, *sdata_tmp;
1308 struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
1309 struct ieee80211_chanctx *new_ctx = NULL;
1310 int i, err, n_assigned, n_reserved, n_ready;
1311 int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
1312
1313 lockdep_assert_held(&local->mtx);
1314 lockdep_assert_held(&local->chanctx_mtx);
1315
1316 /*
1317 * If there are 2 independent pairs of channel contexts performing
1318 * cross-switch of their vifs this code will still wait until both are
1319 * ready even though it could be possible to switch one before the
1320 * other is ready.
1321 *
1322 * For practical reasons and code simplicity just do a single huge
1323 * switch.
1324 */
1325
1326 /*
1327 * Verify if the reservation is still feasible.
1328 * - if it's not then disconnect
1329 * - if it is but not all vifs necessary are ready then defer
1330 */
1331
1332 list_for_each_entry(ctx, &local->chanctx_list, list) {
1333 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1334 continue;
1335
1336 if (WARN_ON(!ctx->replace_ctx)) {
1337 err = -EINVAL;
1338 goto err;
1339 }
1340
1341 if (!local->use_chanctx)
1342 new_ctx = ctx;
1343
1344 n_ctx++;
1345
1346 n_assigned = 0;
1347 n_reserved = 0;
1348 n_ready = 0;
1349
1350 list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
1351 assigned_chanctx_list) {
1352 n_assigned++;
1353 if (sdata->reserved_chanctx) {
1354 n_reserved++;
1355 if (sdata->reserved_ready)
1356 n_ready++;
1357 }
1358 }
1359
1360 if (n_assigned != n_reserved) {
1361 if (n_ready == n_reserved) {
1362 wiphy_info(local->hw.wiphy,
1363 "channel context reservation cannot be finalized because some interfaces aren't switching\n");
1364 err = -EBUSY;
1365 goto err;
1366 }
1367
1368 return -EAGAIN;
1369 }
1370
1371 ctx->conf.radar_enabled = false;
1372 list_for_each_entry(sdata, &ctx->reserved_vifs,
1373 reserved_chanctx_list) {
1374 if (ieee80211_vif_has_in_place_reservation(sdata) &&
1375 !sdata->reserved_ready)
1376 return -EAGAIN;
1377
1378 old_ctx = ieee80211_vif_get_chanctx(sdata);
1379 if (old_ctx) {
1380 if (old_ctx->replace_state ==
1381 IEEE80211_CHANCTX_WILL_BE_REPLACED)
1382 n_vifs_switch++;
1383 else
1384 n_vifs_assign++;
1385 } else {
1386 n_vifs_ctxless++;
1387 }
1388
1389 if (sdata->reserved_radar_required)
1390 ctx->conf.radar_enabled = true;
1391 }
1392 }
1393
1394 if (WARN_ON(n_ctx == 0) ||
1395 WARN_ON(n_vifs_switch == 0 &&
1396 n_vifs_assign == 0 &&
1397 n_vifs_ctxless == 0) ||
1398 WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
1399 WARN_ON(!new_ctx && !local->use_chanctx)) {
1400 err = -EINVAL;
1401 goto err;
1402 }
1403
1404 /*
1405 * All necessary vifs are ready. Perform the switch now depending on
1406 * reservations and driver capabilities.
1407 */
1408
1409 if (local->use_chanctx) {
1410 if (n_vifs_switch > 0) {
1411 err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
1412 if (err)
1413 goto err;
1414 }
1415
1416 if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
1417 err = ieee80211_chsw_switch_ctxs(local);
1418 if (err)
1419 goto err;
1420 }
1421 } else {
1422 err = ieee80211_chsw_switch_hwconf(local, new_ctx);
1423 if (err)
1424 goto err;
1425 }
1426
1427 /*
1428 * Update all structures, values and pointers to point to new channel
1429 * context(s).
1430 */
1431
1432 i = 0;
1433 list_for_each_entry(ctx, &local->chanctx_list, list) {
1434 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1435 continue;
1436
1437 if (WARN_ON(!ctx->replace_ctx)) {
1438 err = -EINVAL;
1439 goto err;
1440 }
1441
1442 list_for_each_entry(sdata, &ctx->reserved_vifs,
1443 reserved_chanctx_list) {
1444 u32 changed = 0;
1445
1446 if (!ieee80211_vif_has_in_place_reservation(sdata))
1447 continue;
1448
1449 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
1450
1451 if (sdata->vif.type == NL80211_IFTYPE_AP)
1452 __ieee80211_vif_copy_chanctx_to_vlans(sdata,
1453 false);
1454
1455 sdata->radar_required = sdata->reserved_radar_required;
1456
1457 if (sdata->vif.bss_conf.chandef.width !=
1458 sdata->reserved_chandef.width)
1459 changed = BSS_CHANGED_BANDWIDTH;
1460
1461 sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
1462 if (changed)
1463 ieee80211_bss_info_change_notify(sdata,
1464 changed);
1465
1466 ieee80211_recalc_txpower(sdata);
1467 }
1468
1469 ieee80211_recalc_chanctx_chantype(local, ctx);
1470 ieee80211_recalc_smps_chanctx(local, ctx);
1471 ieee80211_recalc_radar_chanctx(local, ctx);
1472 ieee80211_recalc_chanctx_min_def(local, ctx);
1473
1474 list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1475 reserved_chanctx_list) {
1476 if (ieee80211_vif_get_chanctx(sdata) != ctx)
1477 continue;
1478
1479 list_del(&sdata->reserved_chanctx_list);
1480 list_move(&sdata->assigned_chanctx_list,
1481 &new_ctx->assigned_vifs);
1482 sdata->reserved_chanctx = NULL;
1483 }
1484
1485 /*
1486 * This context might have been a dependency for an already
1487 * ready re-assign reservation interface that was deferred. Do
1488 * not propagate error to the caller though. The in-place
1489 * reservation for originally requested interface has already
1490 * succeeded at this point.
1491 */
1492 list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1493 reserved_chanctx_list) {
1494 if (WARN_ON(ieee80211_vif_has_in_place_reservation(
1495 sdata)))
1496 continue;
1497
1498 if (WARN_ON(sdata->reserved_chanctx != ctx))
1499 continue;
1500
1501 if (!sdata->reserved_ready)
1502 continue;
1503
1504 if (ieee80211_vif_get_chanctx(sdata))
1505 err = ieee80211_vif_use_reserved_reassign(
1506 sdata);
1507 else
1508 err = ieee80211_vif_use_reserved_assign(sdata);
1509
1510 if (err) {
1511 sdata_info(sdata,
1512 "failed to finalize (re-)assign reservation (err=%d)\n",
1513 err);
1514 ieee80211_vif_unreserve_chanctx(sdata);
1515 cfg80211_stop_iface(local->hw.wiphy,
1516 &sdata->wdev,
1517 GFP_KERNEL);
1518 }
1519 }
1520 }
1521
1522 /*
1523 * Finally free old contexts
1524 */
1525
1526 list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
1527 if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1528 continue;
1529
1530 ctx->replace_ctx->replace_ctx = NULL;
1531 ctx->replace_ctx->replace_state =
1532 IEEE80211_CHANCTX_REPLACE_NONE;
1533
1534 list_del_rcu(&ctx->list);
1535 kfree_rcu(ctx, rcu_head);
1536 }
1537
1538 return 0;
1539
1540err:
1541 list_for_each_entry(ctx, &local->chanctx_list, list) {
1542 if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1543 continue;
1544
1545 list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1546 reserved_chanctx_list)
1547 ieee80211_vif_unreserve_chanctx(sdata);
1548 }
1549
1550 return err;
1551}
1552
1553int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
1554{
1555 struct ieee80211_local *local = sdata->local;
1556 struct ieee80211_chanctx *new_ctx;
1557 struct ieee80211_chanctx *old_ctx;
1558 int err;
1559
1560 lockdep_assert_held(&local->mtx);
1561 lockdep_assert_held(&local->chanctx_mtx);
1562
1563 new_ctx = sdata->reserved_chanctx;
1564 old_ctx = ieee80211_vif_get_chanctx(sdata);
1565
1566 if (WARN_ON(!new_ctx))
1567 return -EINVAL;
1568
1569 if (WARN_ON(new_ctx->replace_state ==
1570 IEEE80211_CHANCTX_WILL_BE_REPLACED))
1571 return -EINVAL;
1572
1573 if (WARN_ON(sdata->reserved_ready))
1574 return -EINVAL;
1575
1576 sdata->reserved_ready = true;
1577
1578 if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
1579 if (old_ctx)
1580 err = ieee80211_vif_use_reserved_reassign(sdata);
1581 else
1582 err = ieee80211_vif_use_reserved_assign(sdata);
1583
1584 if (err)
1585 return err;
1586 }
1587
1588 /*
1589 * In-place reservation may need to be finalized now either if:
1590 * a) sdata is taking part in the swapping itself and is the last one
1591 * b) sdata has switched with a re-assign reservation to an existing
1592 * context readying in-place switching of old_ctx
1593 *
1594 * In case of (b) do not propagate the error up because the requested
1595 * sdata already switched successfully. Just spill an extra warning.
1596 * The ieee80211_vif_use_reserved_switch() already stops all necessary
1597 * interfaces upon failure.
1598 */
1599 if ((old_ctx &&
1600 old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1601 new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
1602 err = ieee80211_vif_use_reserved_switch(local);
1603 if (err && err != -EAGAIN) {
1604 if (new_ctx->replace_state ==
1605 IEEE80211_CHANCTX_REPLACES_OTHER)
1606 return err;
1607
1608 wiphy_info(local->hw.wiphy,
1609 "depending in-place reservation failed (err=%d)\n",
1610 err);
1611 }
1612 }
1613
1614 return 0;
1037} 1615}
1038 1616
1039int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 1617int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
@@ -1043,6 +1621,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1043 struct ieee80211_local *local = sdata->local; 1621 struct ieee80211_local *local = sdata->local;
1044 struct ieee80211_chanctx_conf *conf; 1622 struct ieee80211_chanctx_conf *conf;
1045 struct ieee80211_chanctx *ctx; 1623 struct ieee80211_chanctx *ctx;
1624 const struct cfg80211_chan_def *compat;
1046 int ret; 1625 int ret;
1047 1626
1048 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 1627 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
@@ -1069,11 +1648,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1069 } 1648 }
1070 1649
1071 ctx = container_of(conf, struct ieee80211_chanctx, conf); 1650 ctx = container_of(conf, struct ieee80211_chanctx, conf);
1072 if (!cfg80211_chandef_compatible(&conf->def, chandef)) { 1651
1652 compat = cfg80211_chandef_compatible(&conf->def, chandef);
1653 if (!compat) {
1073 ret = -EINVAL; 1654 ret = -EINVAL;
1074 goto out; 1655 goto out;
1075 } 1656 }
1076 1657
1658 switch (ctx->replace_state) {
1659 case IEEE80211_CHANCTX_REPLACE_NONE:
1660 if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
1661 ret = -EBUSY;
1662 goto out;
1663 }
1664 break;
1665 case IEEE80211_CHANCTX_WILL_BE_REPLACED:
1666 /* TODO: Perhaps the bandwith change could be treated as a
1667 * reservation itself? */
1668 ret = -EBUSY;
1669 goto out;
1670 case IEEE80211_CHANCTX_REPLACES_OTHER:
1671 /* channel context that is going to replace another channel
1672 * context doesn't really exist and shouldn't be assigned
1673 * anywhere yet */
1674 WARN_ON(1);
1675 break;
1676 }
1677
1077 sdata->vif.bss_conf.chandef = *chandef; 1678 sdata->vif.bss_conf.chandef = *chandef;
1078 1679
1079 ieee80211_recalc_chanctx_chantype(local, ctx); 1680 ieee80211_recalc_chanctx_chantype(local, ctx);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f88bd1659cde..c26955f6c14d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -701,6 +701,24 @@ enum ieee80211_chanctx_mode {
701 IEEE80211_CHANCTX_EXCLUSIVE 701 IEEE80211_CHANCTX_EXCLUSIVE
702}; 702};
703 703
704/**
705 * enum ieee80211_chanctx_replace_state - channel context replacement state
706 *
707 * This is used for channel context in-place reservations that require channel
708 * context switch/swap.
709 *
710 * @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place
711 * @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced
712 * by a (not yet registered) channel context pointed by %replace_ctx.
713 * @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context
714 * replaces an existing channel context pointed to by %replace_ctx.
715 */
716enum ieee80211_chanctx_replace_state {
717 IEEE80211_CHANCTX_REPLACE_NONE,
718 IEEE80211_CHANCTX_WILL_BE_REPLACED,
719 IEEE80211_CHANCTX_REPLACES_OTHER,
720};
721
704struct ieee80211_chanctx { 722struct ieee80211_chanctx {
705 struct list_head list; 723 struct list_head list;
706 struct rcu_head rcu_head; 724 struct rcu_head rcu_head;
@@ -708,6 +726,9 @@ struct ieee80211_chanctx {
708 struct list_head assigned_vifs; 726 struct list_head assigned_vifs;
709 struct list_head reserved_vifs; 727 struct list_head reserved_vifs;
710 728
729 enum ieee80211_chanctx_replace_state replace_state;
730 struct ieee80211_chanctx *replace_ctx;
731
711 enum ieee80211_chanctx_mode mode; 732 enum ieee80211_chanctx_mode mode;
712 bool driver_present; 733 bool driver_present;
713 734
@@ -778,6 +799,7 @@ struct ieee80211_sub_if_data {
778 struct ieee80211_chanctx *reserved_chanctx; 799 struct ieee80211_chanctx *reserved_chanctx;
779 struct cfg80211_chan_def reserved_chandef; 800 struct cfg80211_chan_def reserved_chandef;
780 bool reserved_radar_required; 801 bool reserved_radar_required;
802 bool reserved_ready;
781 803
782 /* used to reconfigure hardware SM PS */ 804 /* used to reconfigure hardware SM PS */
783 struct work_struct recalc_smps; 805 struct work_struct recalc_smps;
@@ -1820,9 +1842,9 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
1820 enum ieee80211_chanctx_mode mode, 1842 enum ieee80211_chanctx_mode mode,
1821 bool radar_required); 1843 bool radar_required);
1822int __must_check 1844int __must_check
1823ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, 1845ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata);
1824 u32 *changed);
1825int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); 1846int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
1847int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local);
1826 1848
1827int __must_check 1849int __must_check
1828ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 1850ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e31458201278..1b42aa16ec03 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1698,7 +1698,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1698 if (local->use_chanctx) { 1698 if (local->use_chanctx) {
1699 mutex_lock(&local->chanctx_mtx); 1699 mutex_lock(&local->chanctx_mtx);
1700 list_for_each_entry(ctx, &local->chanctx_list, list) 1700 list_for_each_entry(ctx, &local->chanctx_list, list)
1701 WARN_ON(drv_add_chanctx(local, ctx)); 1701 if (ctx->replace_state !=
1702 IEEE80211_CHANCTX_REPLACES_OTHER)
1703 WARN_ON(drv_add_chanctx(local, ctx));
1702 mutex_unlock(&local->chanctx_mtx); 1704 mutex_unlock(&local->chanctx_mtx);
1703 1705
1704 list_for_each_entry(sdata, &local->interfaces, list) { 1706 list_for_each_entry(sdata, &local->interfaces, list) {
@@ -2972,6 +2974,8 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
2972 num[iftype] = 1; 2974 num[iftype] = 1;
2973 2975
2974 list_for_each_entry(ctx, &local->chanctx_list, list) { 2976 list_for_each_entry(ctx, &local->chanctx_list, list) {
2977 if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
2978 continue;
2975 if (ctx->conf.radar_enabled) 2979 if (ctx->conf.radar_enabled)
2976 radar_detect |= BIT(ctx->conf.def.width); 2980 radar_detect |= BIT(ctx->conf.def.width);
2977 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { 2981 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
@@ -3030,6 +3034,9 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
3030 lockdep_assert_held(&local->chanctx_mtx); 3034 lockdep_assert_held(&local->chanctx_mtx);
3031 3035
3032 list_for_each_entry(ctx, &local->chanctx_list, list) { 3036 list_for_each_entry(ctx, &local->chanctx_list, list) {
3037 if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
3038 continue;
3039
3033 num_different_channels++; 3040 num_different_channels++;
3034 3041
3035 if (ctx->conf.radar_enabled) 3042 if (ctx->conf.radar_enabled)