xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/wlan_mlo_mgr_ap.c (revision 8cfe6b10058a04cafb17eed051f2ddf11bee8931)
1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * DOC: contains MLO manager ap related functionality
20  */
21 #include "wlan_objmgr_vdev_obj.h"
22 #include "wlan_mlo_mgr_ap.h"
23 #include <wlan_mlo_mgr_cmn.h>
24 #include <wlan_mlo_mgr_main.h>
25 #include <wlan_utility.h>
26 #ifdef WLAN_MLO_MULTI_CHIP
27 #include "cdp_txrx_mlo.h"
28 #endif
29 #include "wlan_mlo_mgr_peer.h"
30 
31 #ifdef WLAN_MLO_MULTI_CHIP
32 bool mlo_ap_vdev_attach(struct wlan_objmgr_vdev *vdev,
33 			uint8_t link_id,
34 			uint16_t vdev_count)
35 {
36 	struct wlan_mlo_dev_context *dev_ctx;
37 	uint8_t pr_vdev_ids[WLAN_UMAC_MLO_MAX_VDEVS] = { CDP_INVALID_VDEV_ID };
38 	struct wlan_objmgr_psoc *psoc;
39 	int i;
40 
41 	if (!vdev || !vdev->mlo_dev_ctx || !vdev->mlo_dev_ctx->ap_ctx) {
42 		mlo_err("Invalid input");
43 		return false;
44 	}
45 
46 	psoc = wlan_vdev_get_psoc(vdev);
47 	if (!psoc)
48 		return false;
49 
50 	dev_ctx = vdev->mlo_dev_ctx;
51 	wlan_vdev_set_link_id(vdev, link_id);
52 	wlan_vdev_mlme_set_mlo_vdev(vdev);
53 
54 	/*
55 	 * every link will trigger mlo_ap_vdev_attach,
56 	 * and they should provide the same vdev_count.
57 	 */
58 	mlo_dev_lock_acquire(dev_ctx);
59 	dev_ctx->ap_ctx->num_ml_vdevs = vdev_count;
60 	mlo_dev_lock_release(dev_ctx);
61 
62 	for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
63 		if (dev_ctx->wlan_vdev_list[i])
64 			pr_vdev_ids[i] = wlan_vdev_get_id(dev_ctx->wlan_vdev_list[i]);
65 	}
66 
67 	if (cdp_update_mlo_ptnr_list(wlan_psoc_get_dp_handle(psoc),
68 				pr_vdev_ids, WLAN_UMAC_MLO_MAX_VDEVS,
69 				wlan_vdev_get_id(vdev)) != QDF_STATUS_SUCCESS) {
70 		mlo_debug("Failed to add vdev to partner vdev list, vdev id:%d",
71 			 wlan_vdev_get_id(vdev));
72 	}
73 
74 	return true;
75 }
76 #else
77 bool mlo_ap_vdev_attach(struct wlan_objmgr_vdev *vdev,
78 			uint8_t link_id,
79 			uint16_t vdev_count)
80 {
81 	struct wlan_mlo_dev_context *dev_ctx;
82 
83 	if (!vdev || !vdev->mlo_dev_ctx || !vdev->mlo_dev_ctx->ap_ctx) {
84 		mlo_err("Invalid input");
85 		return false;
86 	}
87 
88 	dev_ctx = vdev->mlo_dev_ctx;
89 	wlan_vdev_set_link_id(vdev, link_id);
90 	wlan_vdev_mlme_set_mlo_vdev(vdev);
91 
92 	/*
93 	 * every link will trigger mlo_ap_vdev_attach,
94 	 * and they should provide the same vdev_count.
95 	 */
96 	mlo_dev_lock_acquire(dev_ctx);
97 	dev_ctx->ap_ctx->num_ml_vdevs = vdev_count;
98 	mlo_dev_lock_release(dev_ctx);
99 
100 	return true;
101 }
102 #endif
103 
104 void mlo_ap_get_vdev_list(struct wlan_objmgr_vdev *vdev,
105 			  uint16_t *vdev_count,
106 			  struct wlan_objmgr_vdev **wlan_vdev_list)
107 {
108 	struct wlan_mlo_dev_context *dev_ctx;
109 	int i;
110 	QDF_STATUS status;
111 
112 	*vdev_count = 0;
113 
114 	if (!vdev || !vdev->mlo_dev_ctx) {
115 		mlo_err("Invalid input");
116 		return;
117 	}
118 
119 	dev_ctx = vdev->mlo_dev_ctx;
120 
121 	mlo_dev_lock_acquire(dev_ctx);
122 	*vdev_count = 0;
123 	for (i = 0; i < QDF_ARRAY_SIZE(dev_ctx->wlan_vdev_list); i++) {
124 		if (dev_ctx->wlan_vdev_list[i] &&
125 		    wlan_vdev_mlme_is_mlo_ap(dev_ctx->wlan_vdev_list[i])) {
126 			status = wlan_objmgr_vdev_try_get_ref(
127 						dev_ctx->wlan_vdev_list[i],
128 						WLAN_MLO_MGR_ID);
129 			if (QDF_IS_STATUS_ERROR(status))
130 				break;
131 			wlan_vdev_list[*vdev_count] =
132 				dev_ctx->wlan_vdev_list[i];
133 			(*vdev_count) += 1;
134 		}
135 	}
136 	mlo_dev_lock_release(dev_ctx);
137 }
138 
139 void mlo_ap_get_active_vdev_list(struct wlan_objmgr_vdev *vdev,
140 				 uint16_t *vdev_count,
141 				 struct wlan_objmgr_vdev **wlan_vdev_list)
142 {
143 	struct wlan_mlo_dev_context *dev_ctx;
144 	int i;
145 	QDF_STATUS status;
146 	struct wlan_objmgr_vdev *partner_vdev = NULL;
147 
148 	*vdev_count = 0;
149 
150 	if (!vdev || !vdev->mlo_dev_ctx) {
151 		mlo_err("Invalid input");
152 		return;
153 	}
154 
155 	dev_ctx = vdev->mlo_dev_ctx;
156 
157 	mlo_dev_lock_acquire(dev_ctx);
158 	*vdev_count = 0;
159 	for (i = 0; i < QDF_ARRAY_SIZE(dev_ctx->wlan_vdev_list); i++) {
160 		partner_vdev = dev_ctx->wlan_vdev_list[i];
161 		if (partner_vdev &&
162 		    wlan_vdev_mlme_is_mlo_ap(partner_vdev)) {
163 			if (wlan_vdev_chan_config_valid(partner_vdev) !=
164 						 QDF_STATUS_SUCCESS)
165 				continue;
166 
167 			status = wlan_objmgr_vdev_try_get_ref(partner_vdev,
168 							      WLAN_MLO_MGR_ID);
169 			if (QDF_IS_STATUS_ERROR(status))
170 				break;
171 			wlan_vdev_list[*vdev_count] = partner_vdev;
172 			(*vdev_count) += 1;
173 		}
174 	}
175 	mlo_dev_lock_release(dev_ctx);
176 }
177 
178 void mlo_ap_get_partner_vdev_list_from_mld(
179 		struct wlan_objmgr_vdev *vdev,
180 		uint16_t *vdev_count,
181 		struct wlan_objmgr_vdev **wlan_vdev_list)
182 {
183 	struct wlan_mlo_dev_context *dev_ctx;
184 	int i;
185 	QDF_STATUS status;
186 
187 	*vdev_count = 0;
188 
189 	if (!vdev || !vdev->mlo_dev_ctx) {
190 		mlo_err("Invalid input");
191 		return;
192 	}
193 
194 	dev_ctx = vdev->mlo_dev_ctx;
195 
196 	mlo_dev_lock_acquire(dev_ctx);
197 	*vdev_count = 0;
198 	for (i = 0; i < QDF_ARRAY_SIZE(dev_ctx->wlan_vdev_list); i++) {
199 		if (dev_ctx->wlan_vdev_list[i] &&
200 		    (QDF_SAP_MODE ==
201 		     wlan_vdev_mlme_get_opmode(dev_ctx->wlan_vdev_list[i]))) {
202 			status = wlan_objmgr_vdev_try_get_ref(
203 						dev_ctx->wlan_vdev_list[i],
204 						WLAN_MLO_MGR_ID);
205 			if (QDF_IS_STATUS_ERROR(status))
206 				break;
207 			wlan_vdev_list[*vdev_count] =
208 				dev_ctx->wlan_vdev_list[i];
209 			(*vdev_count) += 1;
210 		}
211 	}
212 	mlo_dev_lock_release(dev_ctx);
213 }
214 
215 /**
216  * mlo_ap_vdev_is_start_resp_rcvd() - Is start response received on this vdev
217  * @vdev: vdev pointer
218  *
219  * Return: SUCCESS if start response is received, ERROR otherwise.
220  */
221 static QDF_STATUS mlo_ap_vdev_is_start_resp_rcvd(struct wlan_objmgr_vdev *vdev)
222 {
223 	enum wlan_vdev_state state;
224 
225 	if (!vdev) {
226 		mlme_err("vdev is null");
227 		return QDF_STATUS_E_FAILURE;
228 	}
229 
230 	if (!wlan_vdev_mlme_is_mlo_ap(vdev))
231 		return QDF_STATUS_E_FAILURE;
232 
233 	state = wlan_vdev_mlme_get_state(vdev);
234 	if ((state == WLAN_VDEV_S_UP) ||
235 	    (state == WLAN_VDEV_S_DFS_CAC_WAIT) ||
236 	    (state == WLAN_VDEV_S_SUSPEND))
237 		return QDF_STATUS_SUCCESS;
238 
239 	return QDF_STATUS_E_FAILURE;
240 }
241 
242 uint16_t wlan_mlo_ap_get_active_links(struct wlan_objmgr_vdev *vdev)
243 {
244 	uint16_t vdev_count = 0;
245 	struct wlan_mlo_dev_context *dev_ctx;
246 	int i;
247 
248 	if (!vdev || !vdev->mlo_dev_ctx || !vdev->mlo_dev_ctx->ap_ctx) {
249 		mlo_err("Invalid input");
250 		return vdev_count;
251 	}
252 
253 	dev_ctx = vdev->mlo_dev_ctx;
254 
255 	mlo_dev_lock_acquire(dev_ctx);
256 	for (i = 0; i < QDF_ARRAY_SIZE(dev_ctx->wlan_vdev_list); i++) {
257 		if (dev_ctx->wlan_vdev_list[i] && QDF_IS_STATUS_SUCCESS(
258 		    mlo_ap_vdev_is_start_resp_rcvd(dev_ctx->wlan_vdev_list[i])))
259 			vdev_count++;
260 	}
261 
262 	mlo_dev_lock_release(dev_ctx);
263 
264 	return vdev_count;
265 }
266 
267 /**
268  * mlo_is_ap_vdev_up_allowed() - Is mlo ap allowed to come up
269  * @vdev: vdev pointer
270  *
271  * Return: true if given ap is allowed to up, false otherwise.
272  */
273 static bool mlo_is_ap_vdev_up_allowed(struct wlan_objmgr_vdev *vdev)
274 {
275 	uint16_t vdev_count = 0;
276 	bool up_allowed = false;
277 	struct wlan_mlo_dev_context *dev_ctx;
278 
279 	if (!vdev) {
280 		mlo_err("Invalid input");
281 		return up_allowed;
282 	}
283 
284 	dev_ctx = vdev->mlo_dev_ctx;
285 
286 	vdev_count = wlan_mlo_ap_get_active_links(vdev);
287 	if (vdev_count == dev_ctx->ap_ctx->num_ml_vdevs)
288 		up_allowed = true;
289 
290 	return up_allowed;
291 }
292 
293 /**
294  * mlo_pre_link_up() - Carry out preparation before bringing up the link
295  * @vdev: vdev pointer
296  *
297  * Return: true if preparation is done successfully
298  */
299 static bool mlo_pre_link_up(struct wlan_objmgr_vdev *vdev)
300 {
301 	if (!vdev) {
302 		mlo_err("vdev is NULL");
303 		return false;
304 	}
305 
306 	if ((wlan_vdev_mlme_get_state(vdev) == WLAN_VDEV_S_UP) &&
307 	    (wlan_vdev_mlme_get_substate(vdev) ==
308 	     WLAN_VDEV_SS_MLO_SYNC_WAIT))
309 		return true;
310 
311 	return false;
312 }
313 
314 /**
315  * mlo_handle_link_ready() - Check if mlo ap is allowed to up or not.
316  *                           If it is allowed, for every link in the
317  *                           WLAN_VDEV_SS_MLO_SYNC_WAIT state, deliver
318  *                           event WLAN_VDEV_SM_EV_MLO_SYNC_COMPLETE.
319  *
320  * This function is triggered once a link gets start response or enters
321  * WLAN_VDEV_SS_MLO_SYNC_WAIT state
322  *
323  * @vdev: vdev pointer
324  *
325  * Return: true if MLO_SYNC_COMPLETE is posted, else false
326  */
327 static bool mlo_handle_link_ready(struct wlan_objmgr_vdev *vdev)
328 {
329 	struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL};
330 	uint16_t num_links = 0;
331 	uint8_t i;
332 
333 	if (!vdev || !vdev->mlo_dev_ctx) {
334 		mlo_err("Invalid input");
335 		return false;
336 	}
337 
338 	if (!mlo_is_ap_vdev_up_allowed(vdev))
339 		return false;
340 
341 	mlo_ap_get_vdev_list(vdev, &num_links, vdev_list);
342 	if (!num_links || (num_links > QDF_ARRAY_SIZE(vdev_list))) {
343 		mlo_err("Invalid number of VDEVs under AP-MLD");
344 		return false;
345 	}
346 
347 	mlo_ap_lock_acquire(vdev->mlo_dev_ctx->ap_ctx);
348 	for (i = 0; i < num_links; i++) {
349 		if (mlo_pre_link_up(vdev_list[i])) {
350 			if (vdev_list[i] != vdev)
351 				wlan_vdev_mlme_sm_deliver_evt(
352 					vdev_list[i],
353 					WLAN_VDEV_SM_EV_MLO_SYNC_COMPLETE,
354 					0, NULL);
355 		}
356 		/* Release ref taken as part of mlo_ap_get_vdev_list */
357 		mlo_release_vdev_ref(vdev_list[i]);
358 	}
359 	mlo_ap_lock_release(vdev->mlo_dev_ctx->ap_ctx);
360 	return true;
361 }
362 
363 bool mlo_ap_link_sync_wait_notify(struct wlan_objmgr_vdev *vdev)
364 {
365 	return mlo_handle_link_ready(vdev);
366 }
367 
368 void mlo_ap_link_start_rsp_notify(struct wlan_objmgr_vdev *vdev)
369 {
370 	mlo_handle_link_ready(vdev);
371 }
372 
373 void mlo_ap_vdev_detach(struct wlan_objmgr_vdev *vdev)
374 {
375 	if (!vdev || !vdev->mlo_dev_ctx) {
376 		mlo_err("Invalid input");
377 		return;
378 	}
379 	wlan_vdev_mlme_clear_mlo_vdev(vdev);
380 }
381 
382 void mlo_ap_link_down_cmpl_notify(struct wlan_objmgr_vdev *vdev)
383 {
384 	mlo_ap_vdev_detach(vdev);
385 }
386 
387 uint16_t mlo_ap_ml_peerid_alloc(void)
388 {
389 	struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx();
390 	uint16_t i;
391 	uint16_t mlo_peer_id;
392 
393 	ml_peerid_lock_acquire(mlo_ctx);
394 	mlo_peer_id = mlo_ctx->last_mlo_peer_id;
395 	for (i = 0; i < mlo_ctx->max_mlo_peer_id; i++) {
396 		mlo_peer_id = (mlo_peer_id + 1) % mlo_ctx->max_mlo_peer_id;
397 
398 		if (!mlo_peer_id)
399 			continue;
400 
401 		if (qdf_test_bit(mlo_peer_id, mlo_ctx->mlo_peer_id_bmap))
402 			continue;
403 
404 		qdf_set_bit(mlo_peer_id, mlo_ctx->mlo_peer_id_bmap);
405 		break;
406 	}
407 	mlo_ctx->last_mlo_peer_id = mlo_peer_id;
408 	ml_peerid_lock_release(mlo_ctx);
409 
410 	if (i == mlo_ctx->max_mlo_peer_id)
411 		return MLO_INVALID_PEER_ID;
412 
413 	mlo_debug(" ML peer id %d is allocated", mlo_peer_id);
414 
415 	return mlo_peer_id;
416 }
417 
418 void mlo_ap_ml_peerid_free(uint16_t mlo_peer_id)
419 {
420 	struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx();
421 
422 	if ((mlo_peer_id == 0) || (mlo_peer_id == MLO_INVALID_PEER_ID)) {
423 		mlo_err(" ML peer id %d is invalid", mlo_peer_id);
424 		return;
425 	}
426 
427 	if (mlo_peer_id > mlo_ctx->max_mlo_peer_id) {
428 		mlo_err(" ML peer id %d is invalid", mlo_peer_id);
429 		QDF_BUG(0);
430 		return;
431 	}
432 
433 	ml_peerid_lock_acquire(mlo_ctx);
434 	if (qdf_test_bit(mlo_peer_id, mlo_ctx->mlo_peer_id_bmap))
435 		qdf_clear_bit(mlo_peer_id, mlo_ctx->mlo_peer_id_bmap);
436 
437 	ml_peerid_lock_release(mlo_ctx);
438 
439 	mlo_debug(" ML peer id %d is freed", mlo_peer_id);
440 }
441 
442 void mlo_ap_vdev_quiet_set(struct wlan_objmgr_vdev *vdev)
443 {
444 	struct wlan_mlo_dev_context *mld_ctx = vdev->mlo_dev_ctx;
445 	uint8_t idx;
446 
447 	if (!mld_ctx || !wlan_vdev_mlme_is_mlo_ap(vdev))
448 		return;
449 
450 	idx = mlo_get_link_vdev_ix(mld_ctx, vdev);
451 	if (idx == MLO_INVALID_LINK_IDX)
452 		return;
453 
454 	mlo_debug("Quiet set for PSOC:%d vdev:%d",
455 		  wlan_psoc_get_id(wlan_vdev_get_psoc(vdev)),
456 		  wlan_vdev_get_id(vdev));
457 
458 	wlan_util_change_map_index(mld_ctx->ap_ctx->mlo_vdev_quiet_bmap,
459 				   idx, 1);
460 }
461 
462 void mlo_ap_vdev_quiet_clear(struct wlan_objmgr_vdev *vdev)
463 {
464 	struct wlan_mlo_dev_context *mld_ctx = vdev->mlo_dev_ctx;
465 	uint8_t idx;
466 
467 	if (!mld_ctx || !wlan_vdev_mlme_is_mlo_ap(vdev))
468 		return;
469 
470 	idx = mlo_get_link_vdev_ix(mld_ctx, vdev);
471 	if (idx == MLO_INVALID_LINK_IDX)
472 		return;
473 
474 	mlo_debug("Quiet clear for PSOC:%d vdev:%d",
475 		  wlan_psoc_get_id(wlan_vdev_get_psoc(vdev)),
476 		  wlan_vdev_get_id(vdev));
477 
478 	wlan_util_change_map_index(mld_ctx->ap_ctx->mlo_vdev_quiet_bmap,
479 				   idx, 0);
480 }
481 
482 bool mlo_ap_vdev_quiet_is_any_idx_set(struct wlan_objmgr_vdev *vdev)
483 {
484 	struct wlan_mlo_dev_context *mld_ctx = vdev->mlo_dev_ctx;
485 
486 	if (!mld_ctx || !wlan_vdev_mlme_is_mlo_ap(vdev))
487 		return false;
488 
489 	return wlan_util_map_is_any_index_set(
490 			mld_ctx->ap_ctx->mlo_vdev_quiet_bmap,
491 			sizeof(mld_ctx->ap_ctx->mlo_vdev_quiet_bmap));
492 }
493 
494 QDF_STATUS
495 mlo_peer_create_get_frm_buf(
496 		struct wlan_mlo_peer_context *ml_peer,
497 		struct peer_create_notif_s *peer_create,
498 		qdf_nbuf_t frm_buf)
499 {
500 	if (wlan_mlo_peer_is_nawds(ml_peer) ||
501 	    wlan_mlo_peer_is_mesh(ml_peer)) {
502 		peer_create->frm_buf = NULL;
503 		return QDF_STATUS_SUCCESS;
504 	}
505 
506 	if (!frm_buf)
507 		return QDF_STATUS_E_FAILURE;
508 
509 	peer_create->frm_buf = qdf_nbuf_clone(frm_buf);
510 	if (!peer_create->frm_buf)
511 		return QDF_STATUS_E_NOMEM;
512 
513 	return QDF_STATUS_SUCCESS;
514 }
515 
516 #ifdef UMAC_SUPPORT_MLNAWDS
517 void mlo_peer_populate_nawds_params(
518 		struct wlan_mlo_peer_context *ml_peer,
519 		struct mlo_partner_info *ml_info)
520 {
521 	uint8_t i;
522 	uint8_t null_mac[QDF_MAC_ADDR_SIZE] = {0x00, 0x00, 0x00,
523 					       0x00, 0x00, 0x00};
524 	struct mlnawds_config nawds_config;
525 
526 	mlo_peer_lock_acquire(ml_peer);
527 	ml_peer->is_nawds_ml_peer = false;
528 	for (i = 0; i < ml_info->num_partner_links; i++) {
529 		nawds_config = ml_info->partner_link_info[i].nawds_config;
530 		/*
531 		 * if ml_info->partner_link_info[i].nawds_config has valid
532 		 * config(check for non-null mac or non-0 caps), then mark
533 		 * ml_peer's is_nawds_ml_peer true & copy the config
534 		 */
535 		if ((nawds_config.caps) ||
536 		    (qdf_mem_cmp(null_mac,
537 				 nawds_config.mac,
538 				 sizeof(null_mac)))) {
539 			ml_peer->is_nawds_ml_peer = true;
540 			ml_peer->nawds_config[i] = nawds_config;
541 		}
542 	}
543 	mlo_peer_lock_release(ml_peer);
544 }
545 #endif
546 
547 #ifdef MESH_MODE_SUPPORT
548 void mlo_peer_populate_mesh_params(
549 		struct wlan_mlo_peer_context *ml_peer,
550 		struct mlo_partner_info *ml_info)
551 {
552 	uint8_t i;
553 	uint8_t null_mac[QDF_MAC_ADDR_SIZE] = {0};
554 	struct mlnawds_config mesh_config;
555 
556 	mlo_peer_lock_acquire(ml_peer);
557 	ml_peer->is_mesh_ml_peer = false;
558 	for (i = 0; i < ml_info->num_partner_links; i++) {
559 		mesh_config = ml_info->partner_link_info[i].mesh_config;
560 		/*
561 		 * if ml_info->partner_link_info[i].mesh_config has valid
562 		 * config(check for non-null mac or non-0 caps), then mark
563 		 * ml_peer's is_mesh_ml_peer true & copy the config
564 		 */
565 		if ((mesh_config.caps) ||
566 		    (qdf_mem_cmp(null_mac,
567 				 mesh_config.mac,
568 				 sizeof(null_mac)))) {
569 			ml_peer->is_mesh_ml_peer = true;
570 			ml_peer->mesh_config[i] = mesh_config;
571 		}
572 	}
573 	mlo_peer_lock_release(ml_peer);
574 }
575 #endif
576