For chips with host_alloc_ml_id=true (QCN9274 etc.), the host allocates the MLD peer ID up front; ath12k_dp_peer_create() publishes the dp_peer into dp_hw->dp_peers[] using that ID immediately. WCN7850/QCC2072 does not work that way: the firmware picks the ID and only tells the host afterwards via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP, so the publication has to be delayed until the event arrives. Introduce ATH12K_MLO_PEER_ID_PENDING (0xFFFE) as a sentinel for "is_mlo, but ID not yet known". On the firmware-allocates path: - ath12k_mac_op_sta_state(NOTEXIST->NONE) skips ath12k_peer_ml_alloc() and stores PENDING in ahsta->ml_peer_id and dp_params.peer_id; - ath12k_dp_peer_create() skips dp_peer registration until a real ID is known; - ath12k_peer_create() leaves peer->ml_id at INVALID so consumer sites do not treat PENDING as a real ID; - ath12k_peer_ml_free() and ath12k_mac_dp_peer_cleanup() skip the dp_peers[] write and the free_ml_peer_id_map clear when host_alloc_ml_id is false or the ID is still PENDING. The HTT handler change that resolves the PENDING ID is added in a follow-up patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp_peer.c | 23 +++++++++++++++-------- drivers/net/wireless/ath/ath12k/mac.c | 22 ++++++++++++++++------ drivers/net/wireless/ath/ath12k/peer.c | 20 +++++++++++++++++--- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 1f56474efbea..8769b41f5db5 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -72,6 +72,7 @@ #define ATH12K_MAX_MLO_PEERS 256 #define ATH12K_MLO_PEER_ID_INVALID 0xFFFF +#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE #define ATH12K_INVALID_RSSI_FULL -1 #define ATH12K_INVALID_RSSI_EMPTY -128 diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index 47d009a0d61f..2a2eae194007 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -472,7 +472,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr, dp_peer->is_mlo = params->is_mlo; /* - * For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer + * For MLO client, the ML peer ID, either known or PENDING, needs to be + * initialized here since the following logic depends on it. + * * For non-MLO client, host gets link peer ID from firmware and will be * assigned at the time of link peer creation */ @@ -488,13 +490,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr, list_add(&dp_peer->list, &dp_hw->dp_peers_list); /* - * For MLO client, the peer_id for ath12k_dp_peer is allocated by host - * and that peer_id is known at this point, and hence this ath12k_dp_peer - * can be added to the RCU table using the peer_id. - * For non-MLO client, this addition to RCU table shall be done at the - * time of assignment of ath12k_dp_link_peer to ath12k_dp_peer. + * For an MLO client whose ML peer ID is allocated by the host, the + * peer_id is known here and the dp_peer can be added to the RCU + * table using it. For an MLO client on chips where the firmware + * allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the + * RCU table publish is deferred to the + * HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client + * the publish happens later, at the time of assignment of + * ath12k_dp_link_peer to ath12k_dp_peer. */ - if (dp_peer->is_mlo) + if (dp_peer->is_mlo && + dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer); spin_unlock_bh(&dp_hw->peer_lock); @@ -515,7 +521,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr, return; } - if (dp_peer->is_mlo) + if (dp_peer->is_mlo && + dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); list_del(&dp_peer->list); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 8cd5e9b15db5..818eb3aa919e 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1285,7 +1285,9 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah) spin_lock_bh(&dp_hw->peer_lock); list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) { if (dp_peer->is_mlo) { - rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); + if (dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) + rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], + NULL); ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta)); } @@ -7746,11 +7748,19 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, /* ML sta */ if (sta->mlo && !ahsta->links_map && (hweight16(sta->valid_links) == 1)) { - ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah); - if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { - ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM", - sta->addr); - goto exit; + if (ah->host_alloc_ml_id) { + ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah); + if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { + ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM", + sta->addr); + goto exit; + } + } else { + /* + * firmware allocates the ML peer ID and notifies + * the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP + */ + ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_PENDING; } dp_params.is_mlo = true; diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index ae93731b4177..25e4b79f11d6 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -230,7 +230,16 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, /* Fill ML info into created peer */ if (sta->mlo) { ml_peer_id = ahsta->ml_peer_id; - peer->ml_id = ml_peer_id; + /* + * For chips where firmware allocates the ML peer ID, + * ml_peer_id is ATH12K_MLO_PEER_ID_PENDING here. The + * MLO_RX_PEER_MAP HTT event handler fixes up + * peer->ml_id once the ID is known. + */ + if (ml_peer_id == ATH12K_MLO_PEER_ID_PENDING) + peer->ml_id = ATH12K_MLO_PEER_ID_INVALID; + else + peer->ml_id = ml_peer_id; ether_addr_copy(peer->ml_addr, sta->addr); /* the assoc link is considered primary for now */ @@ -285,8 +294,13 @@ void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta) { lockdep_assert_wiphy(ah->hw->wiphy); - if (ahsta->ml_peer_id < - (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)) + /* + * Only devices that allocate the ID on the host own a slot in + * free_ml_peer_id_map. + */ + if (ah->host_alloc_ml_id && + (ahsta->ml_peer_id < + (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID))) clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID, ah->free_ml_peer_id_map); ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; -- 2.25.1