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 #include "wlan_mlo_mgr_main.h" 19 #include "qdf_types.h" 20 #include "wlan_cmn.h" 21 #include "wlan_mlo_mgr_peer.h" 22 #include <wlan_mlo_mgr_ap.h> 23 #include <wlan_mlo_mgr_setup.h> 24 #include <wlan_utility.h> 25 #include <wlan_reg_services_api.h> 26 #include <wlan_mlo_mgr_sta.h> 27 #include <wlan_objmgr_vdev_obj.h> 28 #include <wlan_mgmt_txrx_rx_reo_utils_api.h> 29 /** 30 * struct mlpeer_data: PSOC peers MLO data 31 * @total_rssi: sum of RSSI of all ML peers 32 * @num_ml_peers: Number of ML peer's with this PSOC as TQM 33 * @max_ml_peers: Max ML peers can have this PSOC as TQM 34 * (it is to distribute peers across all PSOCs) 35 * @num_non_ml_peers: Non MLO peers of this PSOC 36 */ 37 struct mlpeer_data { 38 int32_t total_rssi; 39 uint16_t num_ml_peers; 40 uint16_t max_ml_peers; 41 uint16_t num_non_ml_peers; 42 }; 43 44 /** 45 * struct mlo_all_link_rssi: structure to collect TQM params for all PSOCs 46 * @psoc_tqm_parms: It collects peer data for all PSOCs 47 * @num_psocs: Number of PSOCs in the system 48 * @current_psoc_id: current psoc id, it is for iterator 49 */ 50 struct mlo_all_link_rssi { 51 struct mlpeer_data psoc_tqm_parms[WLAN_OBJMGR_MAX_DEVICES]; 52 uint8_t num_psocs; 53 uint8_t current_psoc_id; 54 }; 55 56 /* Invalid TQM/PSOC ID */ 57 #define ML_INVALID_PRIMARY_TQM 0xff 58 /* Congestion value */ 59 #define ML_PRIMARY_TQM_CONGESTION 30 60 /* PTQM migration timeout value in ms */ 61 #define ML_PRIMARY_TQM_MIGRATRION_TIMEOUT 4000 62 /* Link ID used for WDS Bridge*/ 63 #define WDS_BRIDGE_VDEV_LINK_ID (WLAN_LINK_ID_INVALID - 1) 64 65 static void wlan_mlo_peer_get_rssi(struct wlan_objmgr_psoc *psoc, 66 void *obj, void *args) 67 { 68 struct wlan_mlo_peer_context *mlo_peer_ctx; 69 struct wlan_objmgr_peer *peer = (struct wlan_objmgr_peer *)obj; 70 struct mlo_all_link_rssi *rssi_data = (struct mlo_all_link_rssi *)args; 71 struct mlpeer_data *tqm_params = NULL; 72 uint8_t index; 73 74 mlo_peer_ctx = peer->mlo_peer_ctx; 75 index = rssi_data->current_psoc_id; 76 tqm_params = &rssi_data->psoc_tqm_parms[index]; 77 78 if (!wlan_peer_is_mlo(peer) && !mlo_peer_ctx) { 79 if (wlan_peer_get_peer_type(peer) == WLAN_PEER_STA) 80 tqm_params->num_non_ml_peers += 1; 81 return; 82 } 83 84 if (!mlo_peer_ctx) 85 return; 86 87 /* If this psoc is new primary UMAC after migration, 88 * account RSSI on new link 89 */ 90 if (mlo_peer_ctx->migrate_primary_umac_psoc_id == 91 rssi_data->current_psoc_id) { 92 tqm_params->total_rssi += mlo_peer_ctx->avg_link_rssi; 93 tqm_params->num_ml_peers += 1; 94 return; 95 } 96 97 /* If this psoc is not primary UMAC or if TQM migration is happening 98 * from current primary psoc, don't account RSSI 99 */ 100 if (mlo_peer_ctx->primary_umac_psoc_id == rssi_data->current_psoc_id && 101 mlo_peer_ctx->migrate_primary_umac_psoc_id == 102 ML_INVALID_PRIMARY_TQM) { 103 tqm_params->total_rssi += mlo_peer_ctx->avg_link_rssi; 104 tqm_params->num_ml_peers += 1; 105 } 106 } 107 108 static void wlan_get_rssi_data_each_psoc(struct wlan_objmgr_psoc *psoc, 109 void *arg, uint8_t index) 110 { 111 struct mlo_all_link_rssi *rssi_data = (struct mlo_all_link_rssi *)arg; 112 struct mlpeer_data *tqm_params = NULL; 113 114 tqm_params = &rssi_data->psoc_tqm_parms[index]; 115 116 tqm_params->total_rssi = 0; 117 tqm_params->num_ml_peers = 0; 118 tqm_params->num_non_ml_peers = 0; 119 tqm_params->max_ml_peers = MAX_MLO_PEER; 120 121 rssi_data->current_psoc_id = index; 122 rssi_data->num_psocs++; 123 124 wlan_objmgr_iterate_obj_list(psoc, WLAN_PEER_OP, 125 wlan_mlo_peer_get_rssi, rssi_data, 0, 126 WLAN_MLO_MGR_ID); 127 } 128 129 static QDF_STATUS mld_get_link_rssi(struct mlo_all_link_rssi *rssi_data) 130 { 131 rssi_data->num_psocs = 0; 132 133 wlan_objmgr_iterate_psoc_list(wlan_get_rssi_data_each_psoc, 134 rssi_data, WLAN_MLO_MGR_ID); 135 136 return QDF_STATUS_SUCCESS; 137 } 138 139 uint8_t 140 wlan_mld_get_best_primary_umac_w_rssi(struct wlan_mlo_peer_context *ml_peer, 141 struct wlan_objmgr_vdev *link_vdevs[], 142 bool allow_all_links) 143 { 144 struct mlo_all_link_rssi rssi_data; 145 uint8_t i; 146 int32_t avg_rssi[WLAN_OBJMGR_MAX_DEVICES] = {0}; 147 int32_t diff_rssi[WLAN_OBJMGR_MAX_DEVICES] = {0}; 148 int32_t diff_low; 149 bool mld_sta_links[WLAN_OBJMGR_MAX_DEVICES] = {0}; 150 bool mld_no_sta[WLAN_OBJMGR_MAX_DEVICES] = {0}; 151 uint8_t prim_link, id, prim_link_hi; 152 uint8_t num_psocs; 153 struct mlpeer_data *tqm_params = NULL; 154 struct wlan_channel *channel; 155 enum phy_ch_width sec_hi_bw, hi_bw; 156 uint8_t cong = ML_PRIMARY_TQM_CONGESTION; 157 uint16_t mld_ml_sta_count[WLAN_OBJMGR_MAX_DEVICES] = {0}; 158 enum phy_ch_width mld_ch_width[WLAN_OBJMGR_MAX_DEVICES]; 159 uint8_t psoc_w_nosta; 160 uint16_t ml_sta_count = 0; 161 uint32_t total_cap, cap; 162 uint16_t bw; 163 bool group_full[WLAN_OBJMGR_MAX_DEVICES] = {0}; 164 uint16_t group_size[WLAN_OBJMGR_MAX_DEVICES] = {0}; 165 uint16_t grp_size = 0; 166 uint16_t group_full_count = 0; 167 168 mld_get_link_rssi(&rssi_data); 169 170 for (i = 0; i < rssi_data.num_psocs; i++) { 171 tqm_params = &rssi_data.psoc_tqm_parms[i]; 172 173 if (tqm_params->num_ml_peers) 174 avg_rssi[i] = (tqm_params->total_rssi / 175 tqm_params->num_ml_peers); 176 } 177 178 /** 179 * If MLD STA associated to a set of links, choose primary UMAC 180 * from those links only 181 */ 182 num_psocs = 0; 183 psoc_w_nosta = 0; 184 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) 185 mld_ch_width[i] = CH_WIDTH_INVALID; 186 187 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 188 if (!link_vdevs[i]) 189 continue; 190 191 id = wlan_vdev_get_psoc_id(link_vdevs[i]); 192 if (id >= WLAN_OBJMGR_MAX_DEVICES) 193 continue; 194 195 if (!allow_all_links && wlan_vdev_skip_pumac(link_vdevs[i])) { 196 mlo_err("Skip Radio for Primary MLO umac"); 197 mld_sta_links[id] = false; 198 continue; 199 } 200 201 tqm_params = &rssi_data.psoc_tqm_parms[id]; 202 mld_sta_links[id] = true; 203 204 channel = wlan_vdev_mlme_get_bss_chan(link_vdevs[i]); 205 mld_ch_width[id] = channel->ch_width; 206 207 if ((tqm_params->num_ml_peers + 208 tqm_params->num_non_ml_peers) == 0) { 209 /* If this PSOC has no stations */ 210 mld_no_sta[id] = true; 211 psoc_w_nosta++; 212 } 213 214 mld_ml_sta_count[id] = tqm_params->num_ml_peers; 215 /* Update total MLO STA count */ 216 ml_sta_count += tqm_params->num_ml_peers; 217 218 num_psocs++; 219 220 /* If no stations are associated, derive diff rssi 221 * based on psoc id {0-20, 20-40, 40 } so that 222 * stations are distributed across TQMs 223 */ 224 if (!avg_rssi[id]) { 225 diff_rssi[id] = (id * 20); 226 continue; 227 } 228 diff_rssi[id] = (ml_peer->avg_link_rssi >= avg_rssi[id]) ? 229 (ml_peer->avg_link_rssi - avg_rssi[id]) : 230 (avg_rssi[id] - ml_peer->avg_link_rssi); 231 232 } 233 234 prim_link = ML_INVALID_PRIMARY_TQM; 235 236 /* If one of the PSOCs doesn't have any station select that PSOC as 237 * primary TQM. If more than one PSOC have no stations as Primary TQM 238 * the vdev with less bw needs to be selected as Primary TQM 239 */ 240 if (psoc_w_nosta == 1) { 241 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 242 if (mld_no_sta[i]) { 243 prim_link = i; 244 break; 245 } 246 } 247 } else if (psoc_w_nosta > 1) { 248 hi_bw = CH_WIDTH_INVALID; 249 sec_hi_bw = CH_WIDTH_INVALID; 250 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 251 if (!mld_no_sta[i]) 252 continue; 253 254 if (hi_bw == CH_WIDTH_INVALID) { 255 prim_link_hi = i; 256 hi_bw = mld_ch_width[i]; 257 continue; 258 } 259 /* if bw is 320MHZ mark it as highest ch width */ 260 if (mld_ch_width[i] == CH_WIDTH_320MHZ) { 261 prim_link = prim_link_hi; 262 sec_hi_bw = hi_bw; 263 hi_bw = mld_ch_width[i]; 264 prim_link_hi = i; 265 } 266 /* If bw is less than or equal to 160 MHZ 267 * and chwidth is greater than than other link 268 * Mark this link as primary link 269 */ 270 if (mld_ch_width[i] <= CH_WIDTH_160MHZ) { 271 if (hi_bw < mld_ch_width[i]) { 272 /* move high bw to second high bw */ 273 prim_link = prim_link_hi; 274 sec_hi_bw = hi_bw; 275 276 hi_bw = mld_ch_width[i]; 277 prim_link_hi = i; 278 } else if ((sec_hi_bw == CH_WIDTH_INVALID) || 279 (sec_hi_bw < mld_ch_width[i])) { 280 /* update sec high bw */ 281 sec_hi_bw = mld_ch_width[i]; 282 prim_link = i; 283 } 284 } 285 } 286 } else { 287 total_cap = 0; 288 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 289 bw = wlan_reg_get_bw_value(mld_ch_width[i]); 290 total_cap += bw * (100 - cong); 291 } 292 293 group_full_count = 0; 294 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 295 if (!mld_sta_links[i]) 296 continue; 297 298 bw = wlan_reg_get_bw_value(mld_ch_width[i]); 299 cap = bw * (100 - cong); 300 grp_size = (ml_sta_count) * ((cap * 100) / total_cap); 301 group_size[i] = grp_size / 100; 302 if (group_size[i] <= mld_ml_sta_count[i]) { 303 group_full[i] = true; 304 group_full_count++; 305 } 306 } 307 308 if ((num_psocs - group_full_count) == 1) { 309 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 310 if (!mld_sta_links[i]) 311 continue; 312 313 if (group_full[i]) 314 continue; 315 316 prim_link = i; 317 break; 318 } 319 } else { 320 diff_low = 0; 321 /* find min diff, based on it, allocate primary umac */ 322 for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) { 323 if (!mld_sta_links[i]) 324 continue; 325 326 /* First iteration */ 327 if (diff_low == 0) { 328 diff_low = diff_rssi[i]; 329 prim_link = i; 330 } else if (diff_low > diff_rssi[i]) { 331 diff_low = diff_rssi[i]; 332 prim_link = i; 333 } 334 } 335 } 336 } 337 338 if (prim_link != ML_INVALID_PRIMARY_TQM) 339 return prim_link; 340 341 /* If primary link id is not found, return id of 1st available link */ 342 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 343 if (!link_vdevs[i]) 344 continue; 345 346 if (!allow_all_links && wlan_vdev_skip_pumac(link_vdevs[i])) { 347 mlo_debug("Skip Radio for Primary MLO umac"); 348 continue; 349 } 350 id = wlan_vdev_get_psoc_id(link_vdevs[i]); 351 if (id >= WLAN_OBJMGR_MAX_DEVICES) 352 continue; 353 354 return wlan_vdev_get_psoc_id(link_vdevs[i]); 355 } 356 357 return ML_INVALID_PRIMARY_TQM; 358 } 359 360 void mlo_peer_assign_primary_umac( 361 struct wlan_mlo_peer_context *ml_peer, 362 struct wlan_mlo_link_peer_entry *peer_entry) 363 { 364 struct wlan_mlo_link_peer_entry *peer_ent_iter; 365 uint8_t i; 366 uint8_t primary_umac_set = 0; 367 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 368 #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) 369 bool is_central_primary = false; 370 uint8_t bridge_umac_id = -1; 371 uint8_t link_peer_psoc_id; 372 struct wlan_mlo_dev_context *ml_dev = NULL; 373 #endif 374 375 /* If MLD is within single SOC, then assoc link becomes 376 * primary umac 377 */ 378 if (ml_peer->primary_umac_psoc_id == ML_PRIMARY_UMAC_ID_INVAL) { 379 #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) 380 ml_dev = ml_peer->ml_dev; 381 382 if (!ml_dev) { 383 mlo_err("ML dev ctx is NULL"); 384 return; 385 } 386 if (ml_dev->bridge_sta_ctx) { 387 is_central_primary = ml_dev->bridge_sta_ctx->is_force_central_primary; 388 bridge_umac_id = ml_dev->bridge_sta_ctx->bridge_umac_id; 389 } 390 link_peer_psoc_id = wlan_peer_get_psoc_id(peer_entry->link_peer); 391 if (is_central_primary) { 392 if (link_peer_psoc_id == bridge_umac_id) { 393 peer_entry->is_primary = true; 394 ml_peer->primary_umac_psoc_id = bridge_umac_id; 395 } else { 396 peer_entry->is_primary = false; 397 ml_peer->primary_umac_psoc_id = bridge_umac_id; 398 } 399 400 } else { 401 402 #endif 403 if (wlan_peer_mlme_is_assoc_peer(peer_entry->link_peer)) { 404 peer_entry->is_primary = true; 405 ml_peer->primary_umac_psoc_id = 406 wlan_peer_get_psoc_id(peer_entry->link_peer); 407 } else { 408 peer_entry->is_primary = false; 409 } 410 411 #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_MLO_MULTI_CHIP) 412 } 413 #endif 414 } else { 415 if ((wlan_peer_mlme_is_assoc_peer(peer_entry->link_peer)) && 416 (ml_peer->max_links > 1) && 417 (mlo_ctx->force_non_assoc_prim_umac)) { 418 peer_entry->is_primary = false; 419 return; 420 } 421 422 /* If this peer PSOC is not derived as Primary PSOC, 423 * mark is_primary as false 424 */ 425 if (wlan_peer_get_psoc_id(peer_entry->link_peer) != 426 ml_peer->primary_umac_psoc_id) { 427 peer_entry->is_primary = false; 428 return; 429 } 430 431 /* For single SOC, check whether is_primary is set for 432 * other partner peer, then mark is_primary false for this peer 433 */ 434 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 435 peer_ent_iter = &ml_peer->peer_list[i]; 436 437 if (!peer_ent_iter->link_peer) 438 continue; 439 440 /* Check for other link peers */ 441 if (peer_ent_iter == peer_entry) 442 continue; 443 444 if (wlan_peer_get_psoc_id(peer_ent_iter->link_peer) != 445 ml_peer->primary_umac_psoc_id) 446 continue; 447 448 if (peer_ent_iter->is_primary) 449 primary_umac_set = 1; 450 } 451 452 if (primary_umac_set) 453 peer_entry->is_primary = false; 454 else 455 peer_entry->is_primary = true; 456 } 457 } 458 459 static int8_t wlan_vdev_derive_link_rssi(struct wlan_objmgr_vdev *vdev, 460 struct wlan_objmgr_vdev *assoc_vdev, 461 int8_t rssi) 462 { 463 struct wlan_channel *channel, *assoc_channel; 464 uint16_t ch_freq, assoc_freq; 465 uint8_t tx_pow, assoc_tx_pow; 466 int8_t diff_txpow; 467 struct wlan_objmgr_pdev *pdev, *assoc_pdev; 468 uint8_t log10_freq; 469 uint8_t derived_rssi; 470 int16_t ten_derived_rssi; 471 int8_t ten_diff_pl = 0; 472 473 pdev = wlan_vdev_get_pdev(vdev); 474 assoc_pdev = wlan_vdev_get_pdev(assoc_vdev); 475 476 channel = wlan_vdev_get_active_channel(vdev); 477 if (channel) 478 ch_freq = channel->ch_freq; 479 else 480 ch_freq = 1; 481 482 assoc_channel = wlan_vdev_get_active_channel(assoc_vdev); 483 if (assoc_channel) 484 assoc_freq = assoc_channel->ch_freq; 485 else 486 assoc_freq = 1; 487 488 /* 489 * diff of path loss (of two links) = log10(freq1) - log10(freq2) 490 * (since distance is constant) 491 * since log10 is not available, we cameup with approximate ranges 492 */ 493 log10_freq = (ch_freq * 10) / assoc_freq; 494 if ((log10_freq >= 20) && (log10_freq < 30)) 495 ten_diff_pl = 4; /* 0.4 *10 */ 496 else if ((log10_freq >= 11) && (log10_freq < 20)) 497 ten_diff_pl = 1; /* 0.1 *10 */ 498 else if ((log10_freq >= 8) && (log10_freq < 11)) 499 ten_diff_pl = 0; /* 0 *10 */ 500 else if ((log10_freq >= 4) && (log10_freq < 8)) 501 ten_diff_pl = -1; /* -0.1 * 10 */ 502 else if ((log10_freq >= 1) && (log10_freq < 4)) 503 ten_diff_pl = -4; /* -0.4 * 10 */ 504 505 assoc_tx_pow = wlan_reg_get_channel_reg_power_for_freq(assoc_pdev, 506 assoc_freq); 507 tx_pow = wlan_reg_get_channel_reg_power_for_freq(pdev, ch_freq); 508 509 diff_txpow = tx_pow - assoc_tx_pow; 510 511 ten_derived_rssi = (diff_txpow * 10) - ten_diff_pl + (rssi * 10); 512 derived_rssi = ten_derived_rssi / 10; 513 514 return derived_rssi; 515 } 516 517 static void mlo_peer_calculate_avg_rssi( 518 struct wlan_mlo_dev_context *ml_dev, 519 struct wlan_mlo_peer_context *ml_peer, 520 int8_t rssi, 521 struct wlan_objmgr_vdev *assoc_vdev) 522 { 523 int32_t total_rssi = 0; 524 uint8_t num_psocs = 0; 525 uint8_t i; 526 struct wlan_objmgr_vdev *vdev; 527 528 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 529 vdev = ml_dev->wlan_vdev_list[i]; 530 if (!vdev) 531 continue; 532 533 num_psocs++; 534 if (vdev == assoc_vdev) 535 total_rssi += rssi; 536 else 537 total_rssi += wlan_vdev_derive_link_rssi(vdev, 538 assoc_vdev, 539 rssi); 540 } 541 542 if (!num_psocs) 543 return; 544 545 ml_peer->avg_link_rssi = total_rssi / num_psocs; 546 } 547 548 #ifdef WLAN_MLO_MULTI_CHIP 549 int8_t mlo_get_central_umac_id( 550 uint8_t *psoc_ids) 551 { 552 uint8_t prim_psoc_id = -1; 553 uint8_t adjacent = 0; 554 555 /* Some 3 link RDPs have restriction on the primary umac. 556 * Only the link that is adjacent to both the links can be 557 * a primary umac. 558 * Note: it means umac migration is also restricted. 559 */ 560 mlo_chip_adjacent(psoc_ids[0], psoc_ids[1], &adjacent); 561 if (!adjacent) { 562 prim_psoc_id = psoc_ids[2]; 563 } else { 564 mlo_chip_adjacent(psoc_ids[0], psoc_ids[2], &adjacent); 565 if (!adjacent) { 566 prim_psoc_id = psoc_ids[1]; 567 } else { 568 /* If all links are adjacent to each other, 569 * no need to restrict the primary umac. 570 * return failure the caller will handle. 571 */ 572 mlo_chip_adjacent(psoc_ids[1], psoc_ids[2], 573 &adjacent); 574 if (!adjacent) 575 prim_psoc_id = psoc_ids[0]; 576 else 577 return prim_psoc_id; 578 } 579 } 580 581 return prim_psoc_id; 582 } 583 584 QDF_STATUS mlo_check_topology(struct wlan_objmgr_pdev *pdev, 585 struct wlan_objmgr_vdev *vdev, 586 uint8_t aplinks) 587 { 588 struct wlan_mlo_dev_context *ml_dev = vdev->mlo_dev_ctx; 589 struct wlan_objmgr_vdev *vdev_iter = NULL; 590 struct wlan_objmgr_vdev *tmp_vdev = NULL; 591 uint8_t psoc_ids[WLAN_UMAC_MLO_MAX_VDEVS]; 592 uint8_t i, idx = 0; 593 uint8_t bridge_umac; 594 uint8_t adjacent = -1; 595 uint8_t max_soc; 596 uint8_t link_id; 597 bool is_mlo_vdev; 598 599 if (!ml_dev) 600 return QDF_STATUS_E_FAILURE; 601 602 /* Do topology check for STA mode for other modes return Success */ 603 if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE) 604 return QDF_STATUS_SUCCESS; 605 606 max_soc = mlo_get_total_links(pdev); 607 608 if (max_soc != WLAN_UMAC_MLO_MAX_VDEVS) { 609 /* For Devices which has no topology dependency return Success */ 610 return QDF_STATUS_SUCCESS; 611 } 612 613 if (!ml_dev->bridge_sta_ctx) { 614 mlo_err("Bridge STA context Null"); 615 return QDF_STATUS_E_FAILURE; 616 } 617 618 /* Incase of 4-LINK RDP in 3-LINK NON-AP MLD mode there is 619 * restriction to have the primary umac as central in topology. 620 * Note: It also means restriction on umac migration 621 */ 622 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 623 vdev_iter = vdev->mlo_dev_ctx->wlan_vdev_list[i]; 624 if (!vdev_iter) 625 continue; 626 /* Store the psoc_ids of the links */ 627 psoc_ids[idx] = wlan_vdev_get_psoc_id(vdev_iter); 628 idx++; 629 } 630 /* If number of links in AP are greater or equal to STA */ 631 if (aplinks >= idx) { 632 /* Station has 2 links enabled */ 633 /* Check if the primary umac and assoc links can be different*/ 634 if (idx == (WLAN_UMAC_MLO_MAX_PSOC_TOPOLOGY - 1)) { 635 mlo_chip_adjacent(psoc_ids[0], psoc_ids[1], &adjacent); 636 if (adjacent == 1) { 637 mlo_info("pri umac & assoc link can be diff as chips are adj"); 638 return QDF_STATUS_SUCCESS; 639 } else { 640 mlo_info("pri umac & assoc link can be diff but need bridge"); 641 return QDF_STATUS_E_FAILURE; 642 } 643 } 644 /* Check if the primary umac and assoc links are same for 3 link sta*/ 645 if (idx == WLAN_UMAC_MLO_MAX_PSOC_TOPOLOGY) { 646 bridge_umac = mlo_get_central_umac_id(psoc_ids); 647 648 tmp_vdev = mlo_get_link_vdev_from_psoc_id(ml_dev, 649 bridge_umac, 650 false); 651 652 if (!tmp_vdev) 653 return QDF_STATUS_E_FAILURE; 654 655 link_id = tmp_vdev->vdev_mlme.mlo_link_id; 656 if (bridge_umac != -1) { 657 if (wlan_vdev_get_psoc_id(vdev) != bridge_umac) { 658 mlo_err("Central LINK %d Force central as primary umac!! ", 659 bridge_umac); 660 tmp_vdev->vdev_objmgr.mlo_central_vdev = true; 661 ml_dev->bridge_sta_ctx->is_force_central_primary = true; 662 ml_dev->bridge_sta_ctx->bridge_umac_id = bridge_umac; 663 ml_dev->bridge_sta_ctx->bridge_link_id = link_id; 664 wlan_objmgr_vdev_release_ref(tmp_vdev, WLAN_MLO_MGR_ID); 665 return QDF_STATUS_SUCCESS; 666 } 667 } 668 } 669 } else { 670 /* If # of links in AP < then link on Station check for bridge vap */ 671 /* Check case when AP MLD is 2 link and NON-AP MLD is 3 link capable*/ 672 if (idx == WLAN_UMAC_MLO_MAX_PSOC_TOPOLOGY && 673 (aplinks == (WLAN_UMAC_MLO_MAX_PSOC_TOPOLOGY - 1))) { 674 bridge_umac = mlo_get_central_umac_id(psoc_ids); 675 tmp_vdev = mlo_get_link_vdev_from_psoc_id(ml_dev, 676 bridge_umac, 677 false); 678 679 if (!tmp_vdev) 680 return QDF_STATUS_E_FAILURE; 681 682 link_id = tmp_vdev->vdev_mlme.mlo_link_id; 683 if (bridge_umac != -1) { 684 if (wlan_vdev_get_psoc_id(vdev) != bridge_umac) { 685 is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(tmp_vdev); 686 if (is_mlo_vdev) { 687 mlo_err("Central Link %d partipating in Assoc!! ", 688 bridge_umac); 689 } else { 690 mlo_err("Central %d not part of Assoc create bridge!!", 691 bridge_umac); 692 tmp_vdev->vdev_objmgr.mlo_central_vdev = true; 693 ml_dev->bridge_sta_ctx->is_force_central_primary = true; 694 ml_dev->bridge_sta_ctx->bridge_umac_id = bridge_umac; 695 ml_dev->bridge_sta_ctx->bridge_vap_exists = true; 696 ml_dev->bridge_sta_ctx->bridge_link_id = WDS_BRIDGE_VDEV_LINK_ID; 697 } 698 } 699 } 700 } 701 } 702 if (tmp_vdev) 703 wlan_objmgr_vdev_release_ref(tmp_vdev, WLAN_MLO_MGR_ID); 704 return QDF_STATUS_SUCCESS; 705 } 706 707 void mlo_update_partner_bridge_info(struct wlan_mlo_dev_context *ml_dev, 708 struct mlo_partner_info *partner_info) 709 { 710 struct wlan_objmgr_vdev *bridge_vdev = NULL; 711 uint8_t bridge_umac_id = -1; 712 uint8_t bridge_index = partner_info->num_partner_links; 713 714 if (!ml_dev || !ml_dev->bridge_sta_ctx) 715 return; 716 717 bridge_umac_id = ml_dev->bridge_sta_ctx->bridge_umac_id; 718 bridge_vdev = mlo_get_link_vdev_from_psoc_id(ml_dev, bridge_umac_id, false); 719 if (bridge_vdev) { 720 partner_info->partner_link_info[bridge_index].link_id = bridge_vdev->vdev_mlme.mlo_link_id; 721 qdf_mem_copy(&partner_info->partner_link_info[bridge_index].link_addr, 722 wlan_vdev_mlme_get_macaddr(bridge_vdev), sizeof(struct qdf_mac_addr)); 723 /* Account for bridge peer here */ 724 partner_info->num_partner_links++; 725 wlan_objmgr_vdev_release_ref(bridge_vdev, WLAN_MLO_MGR_ID); 726 } 727 } 728 729 bool mlo_is_sta_bridge_vdev(struct wlan_objmgr_vdev *vdev) 730 { 731 struct wlan_mlo_dev_context *ml_dev = NULL; 732 733 if (!vdev) 734 return false; 735 736 ml_dev = vdev->mlo_dev_ctx; 737 738 if (!ml_dev || !ml_dev->bridge_sta_ctx) 739 return false; 740 741 if (vdev->vdev_objmgr.mlo_central_vdev && 742 ml_dev->bridge_sta_ctx->bridge_vap_exists) 743 return true; 744 745 return false; 746 } 747 748 qdf_export_symbol(mlo_is_sta_bridge_vdev); 749 750 bool mlo_sta_bridge_exists(struct wlan_objmgr_vdev *vdev) 751 { 752 struct wlan_mlo_dev_context *ml_dev = NULL; 753 754 if (!vdev) 755 return false; 756 757 ml_dev = vdev->mlo_dev_ctx; 758 759 if (!ml_dev || !ml_dev->bridge_sta_ctx) 760 return false; 761 762 if (ml_dev->bridge_sta_ctx->bridge_vap_exists) 763 return true; 764 765 return false; 766 } 767 768 qdf_export_symbol(mlo_sta_bridge_exists); 769 770 bool mlo_is_force_central_primary(struct wlan_objmgr_vdev *vdev) 771 { 772 struct wlan_mlo_dev_context *ml_dev = NULL; 773 774 if (!vdev) 775 return false; 776 777 ml_dev = vdev->mlo_dev_ctx; 778 779 if (!ml_dev || !ml_dev->bridge_sta_ctx) 780 return false; 781 782 if (ml_dev->bridge_sta_ctx->is_force_central_primary) 783 return true; 784 785 return false; 786 } 787 788 qdf_export_symbol(mlo_is_force_central_primary); 789 790 uint8_t mlo_get_total_links(struct wlan_objmgr_pdev *pdev) 791 { 792 uint8_t ml_grp_id; 793 794 ml_grp_id = wlan_get_mlo_grp_id_from_pdev(pdev); 795 return mlo_setup_get_total_socs(ml_grp_id); 796 } 797 798 static QDF_STATUS mlo_set_3_link_primary_umac( 799 struct wlan_mlo_peer_context *ml_peer, 800 struct wlan_objmgr_vdev *link_vdevs[]) 801 { 802 uint8_t psoc_ids[WLAN_UMAC_MLO_MAX_VDEVS]; 803 int8_t central_umac_id; 804 805 if (ml_peer->max_links != 3) 806 return QDF_STATUS_E_FAILURE; 807 808 /* Some 3 link RDPs have restriction on the primary umac. 809 * Only the link that is adjacent to both the links can be 810 * a primary umac. 811 * Note: it means umac migration is also restricted. 812 */ 813 psoc_ids[0] = wlan_vdev_get_psoc_id(link_vdevs[0]); 814 psoc_ids[1] = wlan_vdev_get_psoc_id(link_vdevs[1]); 815 psoc_ids[2] = wlan_vdev_get_psoc_id(link_vdevs[2]); 816 817 central_umac_id = mlo_get_central_umac_id(psoc_ids); 818 if (central_umac_id != -1) 819 ml_peer->primary_umac_psoc_id = central_umac_id; 820 else 821 return QDF_STATUS_E_FAILURE; 822 823 mlo_peer_assign_primary_umac(ml_peer, 824 &ml_peer->peer_list[0]); 825 826 return QDF_STATUS_SUCCESS; 827 } 828 #else 829 static QDF_STATUS mlo_set_3_link_primary_umac( 830 struct wlan_mlo_peer_context *ml_peer, 831 struct wlan_objmgr_vdev *link_vdevs[]) 832 { 833 return QDF_STATUS_E_FAILURE; 834 } 835 #endif 836 837 QDF_STATUS mlo_peer_allocate_primary_umac( 838 struct wlan_mlo_dev_context *ml_dev, 839 struct wlan_mlo_peer_context *ml_peer, 840 struct wlan_objmgr_vdev *link_vdevs[]) 841 { 842 struct wlan_mlo_link_peer_entry *peer_entry; 843 struct wlan_objmgr_peer *assoc_peer = NULL; 844 int32_t rssi; 845 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 846 uint8_t first_link_id = 0; 847 bool primary_umac_set = false; 848 uint8_t i, psoc_id; 849 850 peer_entry = &ml_peer->peer_list[0]; 851 assoc_peer = peer_entry->link_peer; 852 if (!assoc_peer) 853 return QDF_STATUS_E_FAILURE; 854 855 /* For Station mode, assign assoc peer as primary umac */ 856 if (wlan_peer_get_peer_type(assoc_peer) == WLAN_PEER_AP) { 857 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 858 mlo_info("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT " primary umac soc %d ", 859 ml_dev->mld_id, 860 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 861 ml_peer->primary_umac_psoc_id); 862 863 return QDF_STATUS_SUCCESS; 864 } 865 866 /* Select assoc peer's PSOC as primary UMAC in Multi-chip solution, 867 * 1) for single link MLO connection 868 * 2) if MLD is single chip MLO 869 */ 870 if ((mlo_ctx->force_non_assoc_prim_umac) && 871 (ml_peer->max_links >= 1)) { 872 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 873 if (!link_vdevs[i]) 874 continue; 875 876 if (wlan_peer_get_vdev(assoc_peer) == link_vdevs[i]) 877 continue; 878 psoc_id = wlan_vdev_get_psoc_id(link_vdevs[i]); 879 ml_peer->primary_umac_psoc_id = psoc_id; 880 break; 881 } 882 883 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 884 mlo_info("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT 885 " primary umac soc %d ", ml_dev->mld_id, 886 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 887 ml_peer->primary_umac_psoc_id); 888 889 return QDF_STATUS_SUCCESS; 890 } 891 892 if ((ml_peer->max_links == 1) || 893 (mlo_vdevs_check_single_soc(link_vdevs, ml_peer->max_links))) { 894 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 895 mlo_info("MLD ID %d Assoc peer " QDF_MAC_ADDR_FMT 896 " primary umac soc %d ", ml_dev->mld_id, 897 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 898 ml_peer->primary_umac_psoc_id); 899 900 return QDF_STATUS_SUCCESS; 901 } 902 903 if (mlo_set_3_link_primary_umac(ml_peer, link_vdevs) == 904 QDF_STATUS_SUCCESS) { 905 /* If success then the primary umac is restricted and assigned. 906 * if not, there is no restriction, so just fallthrough 907 */ 908 mlo_info("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT 909 " center primary umac soc %d ", 910 ml_dev->mld_id, 911 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 912 ml_peer->primary_umac_psoc_id); 913 914 return QDF_STATUS_SUCCESS; 915 } 916 917 if (mlo_ctx->mlo_is_force_primary_umac) { 918 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 919 if (!link_vdevs[i]) 920 continue; 921 922 psoc_id = wlan_vdev_get_psoc_id(link_vdevs[i]); 923 if (!first_link_id) 924 first_link_id = psoc_id; 925 926 if (psoc_id == mlo_ctx->mlo_forced_primary_umac_id) { 927 ml_peer->primary_umac_psoc_id = psoc_id; 928 primary_umac_set = true; 929 break; 930 } 931 } 932 933 if (!primary_umac_set) 934 ml_peer->primary_umac_psoc_id = first_link_id; 935 936 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 937 mlo_info("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT " primary umac soc %d ", 938 ml_dev->mld_id, 939 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 940 ml_peer->primary_umac_psoc_id); 941 942 return QDF_STATUS_SUCCESS; 943 } 944 945 rssi = wlan_peer_get_rssi(assoc_peer); 946 mlo_peer_calculate_avg_rssi(ml_dev, ml_peer, rssi, 947 wlan_peer_get_vdev(assoc_peer)); 948 949 ml_peer->primary_umac_psoc_id = 950 wlan_mld_get_best_primary_umac_w_rssi(ml_peer, link_vdevs, false); 951 952 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 953 954 mlo_info("MLD ID %d ML Peer " QDF_MAC_ADDR_FMT " avg RSSI %d primary umac soc %d ", 955 ml_dev->mld_id, 956 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes), 957 ml_peer->avg_link_rssi, ml_peer->primary_umac_psoc_id); 958 959 return QDF_STATUS_SUCCESS; 960 } 961 962 QDF_STATUS mlo_peer_free_primary_umac( 963 struct wlan_mlo_dev_context *ml_dev, 964 struct wlan_mlo_peer_context *ml_peer) 965 { 966 return QDF_STATUS_SUCCESS; 967 } 968 969 #ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE 970 void wlan_objmgr_mlo_update_primary_info(struct wlan_objmgr_peer *peer) 971 { 972 struct wlan_mlo_peer_context *ml_peer = NULL; 973 struct wlan_mlo_link_peer_entry *peer_ent_iter; 974 uint8_t i; 975 976 ml_peer = peer->mlo_peer_ctx; 977 wlan_mlo_peer_wsi_link_delete(ml_peer); 978 ml_peer->primary_umac_psoc_id = wlan_peer_get_psoc_id(peer); 979 980 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 981 peer_ent_iter = &ml_peer->peer_list[i]; 982 983 if (!peer_ent_iter->link_peer) 984 continue; 985 986 if (peer_ent_iter->is_primary) 987 peer_ent_iter->is_primary = false; 988 989 if (peer_ent_iter->link_peer == peer) 990 peer_ent_iter->is_primary = true; 991 } 992 wlan_mlo_peer_wsi_link_add(ml_peer); 993 } 994 995 qdf_export_symbol(wlan_objmgr_mlo_update_primary_info); 996 997 void mlo_mlme_ptqm_migrate_timer_cb(void *arg) 998 { 999 struct wlan_mlo_dev_context *ml_dev = (struct wlan_mlo_dev_context *)arg; 1000 struct wlan_mlo_peer_context *ml_peer = NULL; 1001 uint16_t i = 0; 1002 1003 if (!ml_dev) 1004 return; 1005 1006 /* Check for pending bitmaps and issue disconnect */ 1007 for (i = 0; i < MAX_MLO_PEER_ID; i++) { 1008 if (qdf_test_bit(i, ml_dev->mlo_peer_id_bmap)) { 1009 ml_peer = wlan_mlo_get_mlpeer_by_ml_peerid(ml_dev, i); 1010 if (ml_peer && ml_peer->primary_umac_migration_in_progress) { 1011 ml_peer->primary_umac_migration_in_progress = false; 1012 mlo_err("Issue disconnect for ml peer with ml peer id:%d", i); 1013 wlan_mlo_peer_deauth_init(ml_peer, 1014 NULL, 0); 1015 } 1016 qdf_clear_bit(i, ml_dev->mlo_peer_id_bmap); 1017 } 1018 } 1019 } 1020 1021 /** 1022 * mlo_ptqm_list_peek_head() - Returns the head of linked list 1023 * 1024 * @ptqm_list: Pointer to the list of peer ptqm migrate entries 1025 * 1026 * API to retrieve the head from the list of peer ptqm migrate entries 1027 * 1028 * Return: Pointer to peer ptqm migrate entry 1029 */ 1030 static 1031 struct peer_ptqm_migrate_list_entry *mlo_ptqm_list_peek_head( 1032 qdf_list_t *ptqm_list) 1033 { 1034 struct peer_ptqm_migrate_list_entry *peer_entry; 1035 qdf_list_node_t *peer_node = NULL; 1036 1037 if (qdf_list_peek_front(ptqm_list, &peer_node) != QDF_STATUS_SUCCESS) 1038 return NULL; 1039 1040 peer_entry = qdf_container_of(peer_node, 1041 struct peer_ptqm_migrate_list_entry, 1042 node); 1043 1044 return peer_entry; 1045 } 1046 1047 /** 1048 * mlo_get_next_peer_ctx() - Return next peer ptqm entry from the list 1049 * 1050 * @peer_list: Pointer to the list of peer ptqm migrate entries 1051 * @peer_cur: Pointer to the current peer ptqm entry 1052 * 1053 * API to retrieve the next node from the list of peer ptqm migrate entries 1054 * 1055 * Return: Pointer to peer ptqm migrate entry 1056 */ 1057 static 1058 struct peer_ptqm_migrate_list_entry *mlo_get_next_peer_ctx( 1059 qdf_list_t *peer_list, 1060 struct peer_ptqm_migrate_list_entry *peer_cur) 1061 { 1062 struct peer_ptqm_migrate_list_entry *peer_next; 1063 qdf_list_node_t *node = &peer_cur->node; 1064 qdf_list_node_t *next_node = NULL; 1065 1066 /* This API is invoked with lock acquired, do not add log prints */ 1067 if (!node) 1068 return NULL; 1069 1070 if (qdf_list_peek_next(peer_list, node, &next_node) != 1071 QDF_STATUS_SUCCESS) 1072 return NULL; 1073 1074 peer_next = qdf_container_of(next_node, 1075 struct peer_ptqm_migrate_list_entry, 1076 node); 1077 return peer_next; 1078 } 1079 1080 /** 1081 * wlan_mlo_send_ptqm_migrate_cmd() - API to send WMI to trigger ptqm migration 1082 * @vdev: objmgr vdev object 1083 * @list: peer list to be migrated 1084 * @num_peers_failed: number of peers for which wmi cmd is failed. 1085 * This value is expected to be used only in case failure is returned by WMI 1086 * 1087 * API to send WMI to trigger ptqm migration 1088 * 1089 * Return: QDF_STATUS 1090 */ 1091 static QDF_STATUS 1092 wlan_mlo_send_ptqm_migrate_cmd(struct wlan_objmgr_vdev *vdev, 1093 struct peer_migrate_ptqm_multi_entries *list, 1094 uint16_t *num_peers_failed) 1095 { 1096 struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops; 1097 struct wlan_objmgr_psoc *psoc; 1098 QDF_STATUS status; 1099 struct peer_ptqm_migrate_params param = {0}; 1100 struct peer_ptqm_migrate_entry *peer_list = NULL; 1101 struct peer_ptqm_migrate_list_entry *peer_entry, *next_entry; 1102 struct wlan_mlo_dev_context *ml_dev = NULL; 1103 uint16_t i = 0; 1104 1105 ml_dev = vdev->mlo_dev_ctx; 1106 if (!ml_dev) 1107 return QDF_STATUS_E_FAILURE; 1108 1109 psoc = wlan_vdev_get_psoc(vdev); 1110 if (!psoc) { 1111 mlo_err("null psoc"); 1112 return QDF_STATUS_E_NULL_VALUE; 1113 } 1114 1115 mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops; 1116 if (!mlo_tx_ops || !mlo_tx_ops->peer_ptqm_migrate_send) { 1117 mlo_err("mlo_tx_ops is null!"); 1118 return QDF_STATUS_E_NULL_VALUE; 1119 } 1120 1121 param.vdev_id = wlan_vdev_get_id(vdev); 1122 param.num_peers = list->num_entries; 1123 1124 param.peer_list = qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_entry) * 1125 list->num_entries); 1126 if (!param.peer_list) { 1127 mlo_err("Failed to allocate memory for ptqm migration command"); 1128 return QDF_STATUS_E_FAILURE; 1129 } 1130 1131 peer_list = param.peer_list; 1132 1133 peer_entry = mlo_ptqm_list_peek_head(&list->peer_list); 1134 while (peer_entry) { 1135 peer_list[i].ml_peer_id = peer_entry->mlo_peer_id; 1136 peer_list[i].hw_link_id = peer_entry->new_hw_link_id; 1137 1138 qdf_set_bit(peer_entry->mlo_peer_id, 1139 ml_dev->mlo_peer_id_bmap); 1140 1141 mlo_debug("idx:%d, ml_peer_id:%d, hw_link_id:%d", 1142 i, peer_list[i].ml_peer_id, 1143 peer_list[i].hw_link_id); 1144 i++; 1145 next_entry = mlo_get_next_peer_ctx(&list->peer_list, 1146 peer_entry); 1147 peer_entry = next_entry; 1148 } 1149 1150 status = mlo_tx_ops->peer_ptqm_migrate_send(vdev, ¶m); 1151 if (QDF_IS_STATUS_ERROR(status)) { 1152 mlo_err("Failed to send WMI for ptqm migration"); 1153 *num_peers_failed = param.num_peers_failed; 1154 qdf_mem_free(param.peer_list); 1155 return QDF_STATUS_E_FAILURE; 1156 } 1157 1158 /* Set timeout equal to peer delete timeout as requested by FW. 1159 * Timeout value to be optimized later. Timeout value will be 1160 * updated later based on stress testings. 1161 */ 1162 qdf_timer_mod(&ml_dev->ptqm_migrate_timer, 1163 ML_PRIMARY_TQM_MIGRATRION_TIMEOUT); 1164 1165 qdf_mem_free(param.peer_list); 1166 return QDF_STATUS_SUCCESS; 1167 } 1168 1169 /** 1170 * wlan_mlo_get_new_ptqm_id() - API to get new ptqm ID 1171 * @curr_vdev: objmgr vdev object for current primary link 1172 * @ml_peer: ml peer object 1173 * @new_primary_link_id: new primary link id 1174 * @new_hw_link_id: hw link id for new primary TQM 1175 * @force_mig: allow migration to vdevs which are disabled to be pumac 1176 * using primary_umac_skip ini 1177 * 1178 * API to get new ptqm ID 1179 * 1180 * Return: QDF_STATUS 1181 */ 1182 static QDF_STATUS 1183 wlan_mlo_get_new_ptqm_id(struct wlan_objmgr_vdev *curr_vdev, 1184 struct wlan_mlo_peer_context *ml_peer, 1185 uint8_t new_primary_link_id, 1186 uint16_t *new_hw_link_id, 1187 bool force_mig) 1188 { 1189 uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID; 1190 struct wlan_objmgr_vdev *tmp_vdev = NULL; 1191 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = { NULL }; 1192 struct wlan_objmgr_vdev *tmp_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = { NULL }; 1193 struct wlan_mlo_link_peer_entry *peer_entry; 1194 uint8_t psoc_ids[WLAN_UMAC_MLO_MAX_VDEVS]; 1195 struct wlan_objmgr_vdev *link_vdev = NULL; 1196 struct wlan_objmgr_peer *curr_peer = NULL; 1197 QDF_STATUS status; 1198 uint8_t i = 0, idx = 0, j = 0, tmp_cnt = 0; 1199 1200 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 1201 peer_entry = &ml_peer->peer_list[i]; 1202 if (!peer_entry || !peer_entry->link_peer) 1203 continue; 1204 1205 if (peer_entry->is_primary) { 1206 curr_peer = peer_entry->link_peer; 1207 break; 1208 } 1209 } 1210 1211 if (!curr_peer) { 1212 mlo_err("ML peer " QDF_MAC_ADDR_FMT " current primary link not found", 1213 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1214 return QDF_STATUS_E_INVAL; 1215 } 1216 1217 if (wlan_vdev_mlme_get_opmode(curr_vdev) == QDF_SAP_MODE && 1218 wlan_peer_mlme_get_state(curr_peer) != WLAN_CONNECTED_STATE) { 1219 mlo_err("ML peer " QDF_MAC_ADDR_FMT " is not authorized", 1220 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1221 return QDF_STATUS_E_INVAL; 1222 } 1223 1224 *new_hw_link_id = INVALID_HW_LINK_ID; 1225 current_primary_link_id = 1226 wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer); 1227 if (current_primary_link_id == WLAN_LINK_ID_INVALID) { 1228 mlo_err("ML peer " QDF_MAC_ADDR_FMT "current primary link id is invalid", 1229 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1230 return QDF_STATUS_E_INVAL; 1231 } 1232 1233 if (current_primary_link_id == new_primary_link_id) { 1234 mlo_err("current and requested link_id are same"); 1235 return QDF_STATUS_E_INVAL; 1236 } 1237 1238 if (new_primary_link_id != WLAN_LINK_ID_INVALID) { 1239 link_vdev = mlo_get_vdev_by_link_id(curr_vdev, 1240 new_primary_link_id, 1241 WLAN_MLO_MGR_ID); 1242 if (!link_vdev) { 1243 mlo_err("links vdev not found for link id %d", 1244 new_primary_link_id); 1245 return QDF_STATUS_E_INVAL; 1246 } 1247 1248 if (wlan_vdev_read_skip_pumac_cnt(link_vdev) > 0) { 1249 mlo_err("Selected new ptqm link not allowed for migration"); 1250 mlo_release_vdev_ref(link_vdev); 1251 return QDF_STATUS_E_PERM; 1252 } 1253 mlo_release_vdev_ref(link_vdev); 1254 } 1255 1256 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 1257 peer_entry = &ml_peer->peer_list[i]; 1258 if (!peer_entry || !peer_entry->link_peer) 1259 continue; 1260 1261 if (wlan_peer_get_peer_type(peer_entry->link_peer) == 1262 WLAN_PEER_MLO_BRIDGE) 1263 goto exit; 1264 1265 psoc_ids[j++] = wlan_vdev_get_psoc_id( 1266 wlan_peer_get_vdev(peer_entry->link_peer)); 1267 } 1268 1269 /* For some 3 link RDPs, there is restriction on primary umac */ 1270 if (j == 3) { 1271 if (mlo_get_central_umac_id(psoc_ids) != -1) { 1272 mlo_err("ML peer " QDF_MAC_ADDR_FMT "migration not supported", 1273 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1274 goto exit; 1275 } 1276 } 1277 1278 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 1279 peer_entry = &ml_peer->peer_list[i]; 1280 if (!peer_entry || !peer_entry->link_peer) 1281 continue; 1282 1283 tmp_vdev = wlan_peer_get_vdev(peer_entry->link_peer); 1284 if (!tmp_vdev || tmp_vdev == curr_vdev) 1285 continue; 1286 1287 status = wlan_objmgr_vdev_try_get_ref(tmp_vdev, 1288 WLAN_MLME_SB_ID); 1289 if (QDF_IS_STATUS_ERROR(status)) { 1290 mlo_err("failed to get vdev ref"); 1291 continue; 1292 } 1293 1294 if (wlan_vdev_read_skip_pumac_cnt(tmp_vdev) > 0) { 1295 mlo_debug("Vdev not allowed for migration, skip this vdev"); 1296 wlan_objmgr_vdev_release_ref(tmp_vdev, 1297 WLAN_MLME_SB_ID); 1298 continue; 1299 } 1300 1301 /* Store vdevs which cannot be selected as primary in a temp 1302 * list. force_mig flag will be used to allow migration to vdevs 1303 * which are not allowed to be selected as primary by using the 1304 * primary_umac_skip ini config. This will be helpful in scenarios 1305 * where if the current primary link is going down and peer ptqm 1306 * needs to be migrated but the partner links ofthat mld are 1307 * the user disabled links for ptqm. 1308 */ 1309 if (wlan_vdev_skip_pumac(tmp_vdev)) { 1310 mlo_debug("Vdev cannot be selected as primary"); 1311 tmp_vdev_list[tmp_cnt++] = tmp_vdev; 1312 continue; 1313 } 1314 wlan_vdev_list[idx++] = tmp_vdev; 1315 } 1316 1317 if (new_primary_link_id == WLAN_LINK_ID_INVALID) { 1318 mlo_debug("Invalid link id provided, select new link id"); 1319 /* If there are no vdevs present in wlan_vdev_list, means none 1320 * of the partner vdevs of current MLD are eligible to become 1321 * primary umac. In that case if user has requested force 1322 * migration and there are some vdevs present in temp_vdev_list, 1323 * select new primary umac from those vdev. If no vdevs are 1324 * prenset in any of the list, return failure. 1325 */ 1326 if (idx == 0) { 1327 if (!force_mig || tmp_cnt == 0) { 1328 mlo_err("No link available to be selected as primary"); 1329 goto exit; 1330 } else { 1331 ml_peer->migrate_primary_umac_psoc_id = 1332 wlan_mld_get_best_primary_umac_w_rssi( 1333 ml_peer, 1334 tmp_vdev_list, 1335 true); 1336 if (ml_peer->migrate_primary_umac_psoc_id == 1337 ML_PRIMARY_UMAC_ID_INVAL) { 1338 mlo_err("Unable to fetch new primary link id for ml peer " QDF_MAC_ADDR_FMT, 1339 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1340 goto exit; 1341 } 1342 1343 for (i = 0; i < tmp_cnt; i++) { 1344 if (ml_peer->migrate_primary_umac_psoc_id == 1345 wlan_vdev_get_psoc_id(tmp_vdev_list[i])) { 1346 *new_hw_link_id = wlan_mlo_get_pdev_hw_link_id( 1347 wlan_vdev_get_pdev(tmp_vdev_list[i])); 1348 break; 1349 } 1350 } 1351 } 1352 } else { 1353 ml_peer->migrate_primary_umac_psoc_id = 1354 wlan_mld_get_best_primary_umac_w_rssi( 1355 ml_peer, 1356 wlan_vdev_list, 1357 false); 1358 if (ml_peer->migrate_primary_umac_psoc_id == 1359 ML_PRIMARY_UMAC_ID_INVAL) { 1360 mlo_err("Unable to fetch new primary link id for ml peer " QDF_MAC_ADDR_FMT, 1361 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1362 goto exit; 1363 } 1364 for (i = 0; i < idx; i++) { 1365 if (ml_peer->migrate_primary_umac_psoc_id == 1366 wlan_vdev_get_psoc_id(wlan_vdev_list[i])) { 1367 *new_hw_link_id = wlan_mlo_get_pdev_hw_link_id( 1368 wlan_vdev_get_pdev(wlan_vdev_list[i])); 1369 break; 1370 } 1371 } 1372 } 1373 } else { 1374 /* check if provided link id is part of current ml peer links */ 1375 for (i = 0; i < idx; i++) { 1376 if (new_primary_link_id == wlan_vdev_get_link_id(wlan_vdev_list[i])) { 1377 *new_hw_link_id = wlan_mlo_get_pdev_hw_link_id( 1378 wlan_vdev_get_pdev(wlan_vdev_list[i])); 1379 ml_peer->migrate_primary_umac_psoc_id = 1380 wlan_vdev_get_psoc_id(wlan_vdev_list[i]); 1381 break; 1382 } 1383 } 1384 if (*new_hw_link_id == INVALID_HW_LINK_ID && force_mig) { 1385 for (i = 0; i < tmp_cnt; i++) { 1386 if (new_primary_link_id == 1387 wlan_vdev_get_link_id(tmp_vdev_list[i])) { 1388 *new_hw_link_id = wlan_mlo_get_pdev_hw_link_id( 1389 wlan_vdev_get_pdev(tmp_vdev_list[i])); 1390 ml_peer->migrate_primary_umac_psoc_id = 1391 wlan_vdev_get_psoc_id(tmp_vdev_list[i]); 1392 break; 1393 } 1394 } 1395 } 1396 } 1397 1398 if (*new_hw_link_id == INVALID_HW_LINK_ID) { 1399 mlo_err("New primary link id not found for ml peer " QDF_MAC_ADDR_FMT, 1400 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1401 goto exit; 1402 } 1403 1404 for (i = 0; i < idx; i++) 1405 wlan_objmgr_vdev_release_ref(wlan_vdev_list[i], 1406 WLAN_MLME_SB_ID); 1407 1408 for (i = 0; i < tmp_cnt; i++) 1409 wlan_objmgr_vdev_release_ref(tmp_vdev_list[i], 1410 WLAN_MLME_SB_ID); 1411 return QDF_STATUS_SUCCESS; 1412 1413 exit: 1414 ml_peer->migrate_primary_umac_psoc_id = ML_PRIMARY_UMAC_ID_INVAL; 1415 1416 for (i = 0; i < idx; i++) 1417 wlan_objmgr_vdev_release_ref(wlan_vdev_list[i], 1418 WLAN_MLME_SB_ID); 1419 1420 for (i = 0; i < tmp_cnt; i++) 1421 wlan_objmgr_vdev_release_ref(tmp_vdev_list[i], 1422 WLAN_MLME_SB_ID); 1423 return QDF_STATUS_E_FAILURE; 1424 } 1425 1426 /** 1427 * wlan_mlo_free_ptqm_migrate_list() - API to free peer ptqm migration list 1428 * @list: peer ptqm migration list 1429 * 1430 * API to free peer ptqm migration list 1431 * 1432 * Return: void 1433 */ 1434 static void wlan_mlo_free_ptqm_migrate_list( 1435 struct peer_migrate_ptqm_multi_entries *list) 1436 { 1437 struct peer_ptqm_migrate_list_entry *peer_entry, *next_entry; 1438 1439 peer_entry = mlo_ptqm_list_peek_head(&list->peer_list); 1440 while (peer_entry) { 1441 list->num_entries--; 1442 next_entry = mlo_get_next_peer_ctx(&list->peer_list, 1443 peer_entry); 1444 if (peer_entry->peer) 1445 wlan_objmgr_peer_release_ref(peer_entry->peer, 1446 WLAN_MLME_SB_ID); 1447 qdf_list_remove_node(&list->peer_list, &peer_entry->node); 1448 qdf_mem_free(peer_entry); 1449 peer_entry = next_entry; 1450 } 1451 qdf_list_destroy(&list->peer_list); 1452 } 1453 1454 /** 1455 * wlan_mlo_reset_ptqm_migrate_list() - API to reset peer ptqm migration list 1456 * @ml_dev: MLO dev context 1457 * @list: peer ptqm migration list 1458 * @num_peers_failed: number of peers for which wmi cmd is failed. 1459 * 1460 * API to reset peer ptqm migration list 1461 * 1462 * Return: void 1463 */ 1464 static void wlan_mlo_reset_ptqm_migrate_list( 1465 struct wlan_mlo_dev_context *ml_dev, 1466 struct peer_migrate_ptqm_multi_entries *list, 1467 uint16_t num_peers_failed) 1468 { 1469 struct peer_ptqm_migrate_list_entry *peer_entry, *next_entry; 1470 uint16_t count = 0; 1471 1472 if (!ml_dev) 1473 return; 1474 1475 peer_entry = mlo_ptqm_list_peek_head(&list->peer_list); 1476 while (peer_entry) { 1477 /* Reset the flags only for entries for which wmi 1478 * command trigger is failed 1479 */ 1480 if (count < list->num_entries - num_peers_failed) { 1481 count++; 1482 next_entry = mlo_get_next_peer_ctx(&list->peer_list, 1483 peer_entry); 1484 peer_entry = next_entry; 1485 continue; 1486 } 1487 if (peer_entry->peer) { 1488 qdf_clear_bit(peer_entry->mlo_peer_id, ml_dev->mlo_peer_id_bmap); 1489 peer_entry->peer->mlo_peer_ctx->primary_umac_migration_in_progress = false; 1490 peer_entry->peer->mlo_peer_ctx->migrate_primary_umac_psoc_id = 1491 ML_PRIMARY_UMAC_ID_INVAL; 1492 } 1493 next_entry = mlo_get_next_peer_ctx(&list->peer_list, 1494 peer_entry); 1495 peer_entry = next_entry; 1496 } 1497 } 1498 1499 /** 1500 * wlan_mlo_build_ptqm_migrate_list() - API to build peer ptqm migration list 1501 * @vdev: objmgr vdev list 1502 * @object: peer object 1503 * @arg: list pointer 1504 * 1505 * API to build peer ptqm migration list 1506 * 1507 * Return: void 1508 */ 1509 static void wlan_mlo_build_ptqm_migrate_list(struct wlan_objmgr_vdev *vdev, 1510 void *object, void *arg) 1511 { 1512 struct wlan_objmgr_peer *peer = (struct wlan_objmgr_peer *)object; 1513 struct peer_migrate_ptqm_multi_entries *list = 1514 (struct peer_migrate_ptqm_multi_entries *)arg; 1515 struct peer_ptqm_migrate_list_entry *peer_entry; 1516 struct wlan_mlo_peer_context *ml_peer; 1517 uint16_t new_hw_link_id = INVALID_HW_LINK_ID; 1518 uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID; 1519 QDF_STATUS status; 1520 1521 if (!wlan_peer_is_mlo(peer) || !peer->mlo_peer_ctx) 1522 return; 1523 1524 ml_peer = peer->mlo_peer_ctx; 1525 1526 if (ml_peer->link_peer_cnt == 1) 1527 return; 1528 1529 if (ml_peer->primary_umac_migration_in_progress) { 1530 mlo_err("peer " QDF_MAC_ADDR_FMT " primary umac migration already in progress", 1531 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1532 return; 1533 } 1534 1535 current_primary_link_id = wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer); 1536 if (current_primary_link_id == WLAN_LINK_ID_INVALID || 1537 current_primary_link_id != wlan_vdev_get_link_id(vdev)) { 1538 mlo_debug("peer " QDF_MAC_ADDR_FMT " not having primary on current vdev", 1539 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1540 return; 1541 } 1542 1543 status = wlan_mlo_get_new_ptqm_id(vdev, ml_peer, 1544 WLAN_LINK_ID_INVALID, 1545 &new_hw_link_id, true); 1546 if (QDF_IS_STATUS_ERROR(status)) { 1547 mlo_err("peer " QDF_MAC_ADDR_FMT " unable to get new ptqm id", 1548 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1549 return; 1550 } 1551 ml_peer->primary_umac_migration_in_progress = true; 1552 1553 peer_entry = (struct peer_ptqm_migrate_list_entry *) 1554 qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_list_entry)); 1555 if (!peer_entry) { 1556 mlo_err("peer " QDF_MAC_ADDR_FMT " unable to allocate peer entry", 1557 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1558 return; 1559 } 1560 1561 status = wlan_objmgr_peer_try_get_ref(peer, WLAN_MLME_SB_ID); 1562 peer_entry->peer = peer; 1563 peer_entry->new_hw_link_id = new_hw_link_id; 1564 peer_entry->mlo_peer_id = ml_peer->mlo_peer_id; 1565 qdf_list_insert_back(&list->peer_list, &peer_entry->node); 1566 list->num_entries++; 1567 } 1568 1569 /** 1570 * wlan_mlo_trigger_link_ptqm_migration() - API to trigger ptqm migration 1571 * for a link 1572 * @vdev: objmgr vdev object 1573 * 1574 * API to trigger ptqm migration of all peers having primary on given link 1575 * 1576 * Return: QDF_STATUS 1577 */ 1578 static QDF_STATUS wlan_mlo_trigger_link_ptqm_migration( 1579 struct wlan_objmgr_vdev *vdev) 1580 { 1581 struct peer_migrate_ptqm_multi_entries migrate_list = {0}; 1582 QDF_STATUS status; 1583 uint16_t num_peers_failed = 0; 1584 1585 qdf_list_create(&migrate_list.peer_list, MAX_MLO_PEER_ID); 1586 wlan_objmgr_iterate_peerobj_list(vdev, 1587 wlan_mlo_build_ptqm_migrate_list, 1588 &migrate_list, WLAN_MLME_NB_ID); 1589 1590 /* trigger WMI */ 1591 if (migrate_list.num_entries == 0) { 1592 mlo_err("No peer found"); 1593 return QDF_STATUS_SUCCESS; 1594 } 1595 1596 status = wlan_mlo_send_ptqm_migrate_cmd(vdev, &migrate_list, 1597 &num_peers_failed); 1598 if (QDF_IS_STATUS_ERROR(status)) 1599 wlan_mlo_reset_ptqm_migrate_list(vdev->mlo_dev_ctx, 1600 &migrate_list, 1601 num_peers_failed); 1602 wlan_mlo_free_ptqm_migrate_list(&migrate_list); 1603 return status; 1604 } 1605 1606 QDF_STATUS wlan_mlo_set_ptqm_migration(struct wlan_objmgr_vdev *vdev, 1607 struct wlan_mlo_peer_context *ml_peer, 1608 bool link_migration, 1609 uint32_t link_id, bool force_mig) 1610 { 1611 uint16_t new_hw_link_id = INVALID_HW_LINK_ID; 1612 struct peer_migrate_ptqm_multi_entries migrate_list = {0}; 1613 struct peer_ptqm_migrate_list_entry *peer_entry; 1614 struct wlan_objmgr_vdev *curr_vdev = NULL; 1615 uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID; 1616 uint16_t num_peers_failed = 0; 1617 QDF_STATUS status; 1618 1619 if (!vdev) { 1620 mlo_err("Vdev is NULL"); 1621 return QDF_STATUS_E_NULL_VALUE; 1622 } 1623 1624 if (link_migration == false && !ml_peer) { 1625 mlo_err("ML peer is NULL"); 1626 return QDF_STATUS_E_NULL_VALUE; 1627 } 1628 1629 if (link_migration) { 1630 mlo_info("Trigger migration for full link"); 1631 // trigger full link migration 1632 status = wlan_mlo_trigger_link_ptqm_migration(vdev); 1633 if (QDF_IS_STATUS_ERROR(status)) 1634 mlo_err("Failed to trigger link migration"); 1635 return status; 1636 } 1637 1638 if (ml_peer->link_peer_cnt == 1) { 1639 mlo_info("peer " QDF_MAC_ADDR_FMT " is SLO", 1640 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1641 return QDF_STATUS_E_FAILURE; 1642 } 1643 1644 if (ml_peer->primary_umac_migration_in_progress) { 1645 mlo_info("peer " QDF_MAC_ADDR_FMT " primary umac migration already in progress", 1646 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1647 return QDF_STATUS_E_FAILURE; 1648 } 1649 1650 current_primary_link_id = wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer); 1651 if (current_primary_link_id == WLAN_LINK_ID_INVALID) { 1652 mlo_err("Current primary link id is invalid"); 1653 return QDF_STATUS_E_FAILURE; 1654 } 1655 1656 curr_vdev = mlo_get_vdev_by_link_id(vdev, current_primary_link_id, 1657 WLAN_MLO_MGR_ID); 1658 if (!curr_vdev) { 1659 mlo_err("Unable to get current primary vdev"); 1660 return QDF_STATUS_E_NULL_VALUE; 1661 } 1662 1663 status = wlan_mlo_get_new_ptqm_id(curr_vdev, ml_peer, 1664 link_id, &new_hw_link_id, force_mig); 1665 if (QDF_IS_STATUS_ERROR(status)) { 1666 mlo_err("peer " QDF_MAC_ADDR_FMT " unable to get new ptqm id", 1667 QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes)); 1668 goto exit; 1669 } 1670 ml_peer->primary_umac_migration_in_progress = true; 1671 1672 peer_entry = (struct peer_ptqm_migrate_list_entry *) 1673 qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_list_entry)); 1674 if (!peer_entry) { 1675 mlo_err("Failed to allocate peer entry"); 1676 status = QDF_STATUS_E_NULL_VALUE; 1677 goto exit; 1678 } 1679 1680 peer_entry->new_hw_link_id = new_hw_link_id; 1681 peer_entry->mlo_peer_id = ml_peer->mlo_peer_id; 1682 qdf_list_create(&migrate_list.peer_list, MAX_MLO_PEER_ID); 1683 qdf_list_insert_back(&migrate_list.peer_list, &peer_entry->node); 1684 migrate_list.num_entries = 1; 1685 1686 //trigger WMI 1687 status = wlan_mlo_send_ptqm_migrate_cmd(curr_vdev, &migrate_list, 1688 &num_peers_failed); 1689 if (QDF_IS_STATUS_ERROR(status)) 1690 wlan_mlo_reset_ptqm_migrate_list(curr_vdev->mlo_dev_ctx, 1691 &migrate_list, 1692 num_peers_failed); 1693 wlan_mlo_free_ptqm_migrate_list(&migrate_list); 1694 1695 exit: 1696 if (curr_vdev) 1697 mlo_release_vdev_ref(curr_vdev); 1698 1699 return status; 1700 } 1701 #endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */ 1702