aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/chan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r--net/mac80211/chan.c765
1 files changed, 683 insertions, 82 deletions
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);