Merge tag 'media/v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-block.git] / net / mac80211 / link.c
CommitLineData
e73b5e51
JB
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * MLO link handling
4 *
5 * Copyright (C) 2022 Intel Corporation
6 */
7#include <linux/slab.h>
8#include <linux/kernel.h>
9#include <net/mac80211.h>
10#include "ieee80211_i.h"
11#include "driver-ops.h"
3d901102 12#include "key.h"
e73b5e51
JB
13
14void ieee80211_link_setup(struct ieee80211_link_data *link)
15{
16 if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
17 ieee80211_mgd_setup_link(link);
18}
19
20void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
21 int link_id,
22 struct ieee80211_link_data *link,
23 struct ieee80211_bss_conf *link_conf)
24{
25 bool deflink = link_id < 0;
26
27 if (link_id < 0)
28 link_id = 0;
29
30 rcu_assign_pointer(sdata->vif.link_conf[link_id], link_conf);
31 rcu_assign_pointer(sdata->link[link_id], link);
32
33 link->sdata = sdata;
34 link->link_id = link_id;
35 link->conf = link_conf;
36 link_conf->link_id = link_id;
37
38 INIT_WORK(&link->csa_finalize_work,
39 ieee80211_csa_finalize_work);
40 INIT_WORK(&link->color_change_finalize_work,
41 ieee80211_color_change_finalize_work);
92881884
LB
42 INIT_DELAYED_WORK(&link->color_collision_detect_work,
43 ieee80211_color_collision_detection_work);
e73b5e51
JB
44 INIT_LIST_HEAD(&link->assigned_chanctx_list);
45 INIT_LIST_HEAD(&link->reserved_chanctx_list);
46 INIT_DELAYED_WORK(&link->dfs_cac_timer_work,
47 ieee80211_dfs_cac_timer_work);
48
49 if (!deflink) {
50 switch (sdata->vif.type) {
51 case NL80211_IFTYPE_AP:
52 ether_addr_copy(link_conf->addr,
53 sdata->wdev.links[link_id].addr);
54 link_conf->bssid = link_conf->addr;
55 WARN_ON(!(sdata->wdev.valid_links & BIT(link_id)));
56 break;
57 case NL80211_IFTYPE_STATION:
58 /* station sets the bssid in ieee80211_mgd_setup_link */
59 break;
60 default:
61 WARN_ON(1);
62 }
63 }
64}
65
66void ieee80211_link_stop(struct ieee80211_link_data *link)
67{
68 if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
69 ieee80211_mgd_stop_link(link);
70
92881884 71 cancel_delayed_work_sync(&link->color_collision_detect_work);
e73b5e51
JB
72 ieee80211_link_release_channel(link);
73}
74
75struct link_container {
76 struct ieee80211_link_data data;
77 struct ieee80211_bss_conf conf;
78};
79
efe9c2bf
JB
80static void ieee80211_tear_down_links(struct ieee80211_sub_if_data *sdata,
81 struct link_container **links, u16 mask)
e73b5e51 82{
efe9c2bf 83 struct ieee80211_link_data *link;
e73b5e51
JB
84 LIST_HEAD(keys);
85 unsigned int link_id;
86
87 for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
efe9c2bf
JB
88 if (!(mask & BIT(link_id)))
89 continue;
90 link = &links[link_id]->data;
91 if (link_id == 0 && !link)
92 link = &sdata->deflink;
93 if (WARN_ON(!link))
e73b5e51 94 continue;
efe9c2bf
JB
95 ieee80211_remove_link_keys(link, &keys);
96 ieee80211_link_stop(link);
e73b5e51
JB
97 }
98
99 synchronize_rcu();
100
101 ieee80211_free_key_list(sdata->local, &keys);
efe9c2bf 102}
e73b5e51 103
efe9c2bf
JB
104static void ieee80211_free_links(struct ieee80211_sub_if_data *sdata,
105 struct link_container **links)
106{
107 unsigned int link_id;
108
109 for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
e73b5e51 110 kfree(links[link_id]);
e73b5e51
JB
111}
112
113static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
114{
115 unsigned int i, j;
116
117 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
118 struct ieee80211_link_data *link1;
119
120 link1 = sdata_dereference(sdata->link[i], sdata);
121 if (!link1)
122 continue;
123 for (j = i + 1; j < IEEE80211_MLD_MAX_NUM_LINKS; j++) {
124 struct ieee80211_link_data *link2;
125
126 link2 = sdata_dereference(sdata->link[j], sdata);
127 if (!link2)
128 continue;
129
130 if (ether_addr_equal(link1->conf->addr,
131 link2->conf->addr))
132 return -EALREADY;
133 }
134 }
135
136 return 0;
137}
138
efe9c2bf
JB
139static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
140 u16 links)
141{
142 sdata->vif.valid_links = links;
143
144 if (!links) {
145 sdata->vif.active_links = 0;
146 return;
147 }
148
149 switch (sdata->vif.type) {
150 case NL80211_IFTYPE_AP:
151 /* in an AP all links are always active */
152 sdata->vif.active_links = links;
153 break;
154 case NL80211_IFTYPE_STATION:
155 if (sdata->vif.active_links)
156 break;
157 WARN_ON(hweight16(links) > 1);
158 sdata->vif.active_links = links;
159 break;
160 default:
161 WARN_ON(1);
162 }
163}
164
e73b5e51
JB
165static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
166 struct link_container **to_free,
167 u16 new_links)
168{
169 u16 old_links = sdata->vif.valid_links;
efe9c2bf 170 u16 old_active = sdata->vif.active_links;
e73b5e51
JB
171 unsigned long add = new_links & ~old_links;
172 unsigned long rem = old_links & ~new_links;
173 unsigned int link_id;
174 int ret;
175 struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link;
176 struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS];
177 struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS];
178 bool use_deflink = old_links == 0; /* set for error case */
179
180 sdata_assert_lock(sdata);
181
182 memset(to_free, 0, sizeof(links));
183
184 if (old_links == new_links)
185 return 0;
186
187 /* if there were no old links, need to clear the pointers to deflink */
188 if (!old_links)
189 rem |= BIT(0);
190
191 /* allocate new link structures first */
192 for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
193 link = kzalloc(sizeof(*link), GFP_KERNEL);
194 if (!link) {
195 ret = -ENOMEM;
196 goto free;
197 }
198 links[link_id] = link;
199 }
200
201 /* keep track of the old pointers for the driver */
202 BUILD_BUG_ON(sizeof(old) != sizeof(sdata->vif.link_conf));
203 memcpy(old, sdata->vif.link_conf, sizeof(old));
204 /* and for us in error cases */
205 BUILD_BUG_ON(sizeof(old_data) != sizeof(sdata->link));
206 memcpy(old_data, sdata->link, sizeof(old_data));
207
208 /* grab old links to free later */
209 for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
210 if (rcu_access_pointer(sdata->link[link_id]) != &sdata->deflink) {
211 /*
212 * we must have allocated the data through this path so
213 * we know we can free both at the same time
214 */
215 to_free[link_id] = container_of(rcu_access_pointer(sdata->link[link_id]),
216 typeof(*links[link_id]),
217 data);
218 }
219
220 RCU_INIT_POINTER(sdata->link[link_id], NULL);
221 RCU_INIT_POINTER(sdata->vif.link_conf[link_id], NULL);
222 }
223
224 /* link them into data structures */
225 for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
226 WARN_ON(!use_deflink &&
227 rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink);
228
229 link = links[link_id];
230 ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
231 ieee80211_link_setup(&link->data);
232 }
233
234 if (new_links == 0)
235 ieee80211_link_init(sdata, -1, &sdata->deflink,
236 &sdata->vif.bss_conf);
237
e73b5e51
JB
238 ret = ieee80211_check_dup_link_addrs(sdata);
239 if (!ret) {
efe9c2bf
JB
240 /* for keys we will not be able to undo this */
241 ieee80211_tear_down_links(sdata, to_free, rem);
242
243 ieee80211_set_vif_links_bitmaps(sdata, new_links);
244
e73b5e51
JB
245 /* tell the driver */
246 ret = drv_change_vif_links(sdata->local, sdata,
efe9c2bf
JB
247 old_links & old_active,
248 new_links & sdata->vif.active_links,
e73b5e51
JB
249 old);
250 }
251
252 if (ret) {
253 /* restore config */
254 memcpy(sdata->link, old_data, sizeof(old_data));
255 memcpy(sdata->vif.link_conf, old, sizeof(old));
efe9c2bf 256 ieee80211_set_vif_links_bitmaps(sdata, old_links);
e73b5e51
JB
257 /* and free (only) the newly allocated links */
258 memset(to_free, 0, sizeof(links));
259 goto free;
260 }
261
262 /* use deflink/bss_conf again if and only if there are no more links */
263 use_deflink = new_links == 0;
264
265 goto deinit;
266free:
267 /* if we failed during allocation, only free all */
268 for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
269 kfree(links[link_id]);
270 links[link_id] = NULL;
271 }
272deinit:
273 if (use_deflink)
274 ieee80211_link_init(sdata, -1, &sdata->deflink,
275 &sdata->vif.bss_conf);
276 return ret;
277}
278
279int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
280 u16 new_links)
281{
282 struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
283 int ret;
284
285 ret = ieee80211_vif_update_links(sdata, links, new_links);
286 ieee80211_free_links(sdata, links);
287
288 return ret;
289}
290
291void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
292{
293 struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
294
295 /*
296 * The locking here is different because when we free links
297 * in the station case we need to be able to cancel_work_sync()
298 * something that also takes the lock.
299 */
300
301 sdata_lock(sdata);
302 ieee80211_vif_update_links(sdata, links, 0);
303 sdata_unlock(sdata);
304
305 ieee80211_free_links(sdata, links);
306}
3d901102
JB
307
308static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
309 u16 active_links)
310{
311 struct ieee80211_bss_conf *link_confs[IEEE80211_MLD_MAX_NUM_LINKS];
312 struct ieee80211_local *local = sdata->local;
313 u16 old_active = sdata->vif.active_links;
314 unsigned long rem = old_active & ~active_links;
315 unsigned long add = active_links & ~old_active;
316 struct sta_info *sta;
317 unsigned int link_id;
318 int ret, i;
319
320 if (!ieee80211_sdata_running(sdata))
321 return -ENETDOWN;
322
323 if (sdata->vif.type != NL80211_IFTYPE_STATION)
324 return -EINVAL;
325
326 /* cannot activate links that don't exist */
327 if (active_links & ~sdata->vif.valid_links)
328 return -EINVAL;
329
330 /* nothing to do */
331 if (old_active == active_links)
332 return 0;
333
334 for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
335 link_confs[i] = sdata_dereference(sdata->vif.link_conf[i],
336 sdata);
337
338 if (add) {
339 sdata->vif.active_links |= active_links;
340 ret = drv_change_vif_links(local, sdata,
341 old_active,
342 sdata->vif.active_links,
343 link_confs);
344 if (ret) {
345 sdata->vif.active_links = old_active;
346 return ret;
347 }
348 }
349
350 for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
351 struct ieee80211_link_data *link;
352
353 link = sdata_dereference(sdata->link[link_id], sdata);
354
355 /* FIXME: kill TDLS connections on the link */
356
357 ieee80211_link_release_channel(link);
358 }
359
360 list_for_each_entry(sta, &local->sta_list, list) {
361 if (sdata != sta->sdata)
362 continue;
9b41a9d7
JB
363
364 /* this is very temporary, but do it anyway */
365 __ieee80211_sta_recalc_aggregates(sta,
366 old_active | active_links);
367
3d901102
JB
368 ret = drv_change_sta_links(local, sdata, &sta->sta,
369 old_active,
370 old_active | active_links);
371 WARN_ON_ONCE(ret);
372 }
373
374 ret = ieee80211_key_switch_links(sdata, rem, add);
375 WARN_ON_ONCE(ret);
376
377 list_for_each_entry(sta, &local->sta_list, list) {
378 if (sdata != sta->sdata)
379 continue;
9b41a9d7
JB
380
381 __ieee80211_sta_recalc_aggregates(sta, active_links);
382
3d901102
JB
383 ret = drv_change_sta_links(local, sdata, &sta->sta,
384 old_active | active_links,
385 active_links);
386 WARN_ON_ONCE(ret);
9b41a9d7
JB
387
388 /*
389 * Do it again, just in case - the driver might very
390 * well have called ieee80211_sta_recalc_aggregates()
391 * from there when filling in the new links, which
392 * would set it wrong since the vif's active links are
393 * not switched yet...
394 */
395 __ieee80211_sta_recalc_aggregates(sta, active_links);
3d901102
JB
396 }
397
398 for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
399 struct ieee80211_link_data *link;
400
401 link = sdata_dereference(sdata->link[link_id], sdata);
402
403 ret = ieee80211_link_use_channel(link, &link->conf->chandef,
404 IEEE80211_CHANCTX_SHARED);
405 WARN_ON_ONCE(ret);
406
407 ieee80211_link_info_change_notify(sdata, link,
408 BSS_CHANGED_ERP_CTS_PROT |
409 BSS_CHANGED_ERP_PREAMBLE |
410 BSS_CHANGED_ERP_SLOT |
411 BSS_CHANGED_HT |
412 BSS_CHANGED_BASIC_RATES |
413 BSS_CHANGED_BSSID |
414 BSS_CHANGED_CQM |
415 BSS_CHANGED_QOS |
416 BSS_CHANGED_TXPOWER |
417 BSS_CHANGED_BANDWIDTH |
418 BSS_CHANGED_TWT |
419 BSS_CHANGED_HE_OBSS_PD |
420 BSS_CHANGED_HE_BSS_COLOR);
421 ieee80211_mgd_set_link_qos_params(link);
422 }
423
424 old_active = sdata->vif.active_links;
425 sdata->vif.active_links = active_links;
426
427 if (rem) {
428 ret = drv_change_vif_links(local, sdata, old_active,
429 active_links, link_confs);
430 WARN_ON_ONCE(ret);
431 }
432
433 return 0;
434}
435
436int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links)
437{
438 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
439 struct ieee80211_local *local = sdata->local;
440 u16 old_active;
441 int ret;
442
443 sdata_lock(sdata);
444 mutex_lock(&local->sta_mtx);
445 mutex_lock(&local->mtx);
446 mutex_lock(&local->key_mtx);
447 old_active = sdata->vif.active_links;
448 if (old_active & active_links) {
449 /*
450 * if there's at least one link that stays active across
451 * the change then switch to it (to those) first, and
452 * then enable the additional links
453 */
454 ret = _ieee80211_set_active_links(sdata,
455 old_active & active_links);
456 if (!ret)
457 ret = _ieee80211_set_active_links(sdata, active_links);
458 } else {
459 /* otherwise switch directly */
460 ret = _ieee80211_set_active_links(sdata, active_links);
461 }
462 mutex_unlock(&local->key_mtx);
463 mutex_unlock(&local->mtx);
464 mutex_unlock(&local->sta_mtx);
465 sdata_unlock(sdata);
466
467 return ret;
468}
469EXPORT_SYMBOL_GPL(ieee80211_set_active_links);
470
471void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
472 u16 active_links)
473{
474 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
475
476 if (!ieee80211_sdata_running(sdata))
477 return;
478
479 if (sdata->vif.type != NL80211_IFTYPE_STATION)
480 return;
481
482 /* cannot activate links that don't exist */
483 if (active_links & ~sdata->vif.valid_links)
484 return;
485
486 /* nothing to do */
487 if (sdata->vif.active_links == active_links)
488 return;
489
490 sdata->desired_active_links = active_links;
491 schedule_work(&sdata->activate_links_work);
492}
493EXPORT_SYMBOL_GPL(ieee80211_set_active_links_async);