]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: ath12k: defer dp_peer registration when firmware allocates MLD peer ID
authorBaochen Qiang <baochen.qiang@oss.qualcomm.com>
Mon, 20 Jul 2026 06:43:28 +0000 (14:43 +0800)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Mon, 27 Jul 2026 15:00:43 +0000 (08:00 -0700)
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 <baochen.qiang@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-7-630632758a80@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/dp_peer.c
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/peer.c

index 1f56474efbea4511d5f8f0e1be4fa747380fea75..8769b41f5db5dce4390d035ea6514caac76ac1ca 100644 (file)
@@ -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
index a12073afc307cea39dee50ee77b4fe2cb17f4891..cd6a0eb207bd8b5eb136bdc4cb99bf70c3c375eb 100644 (file)
@@ -475,7 +475,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
         */
@@ -491,13 +493,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);
@@ -518,7 +524,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);
index 1004c290e5d0e015bbfa3e63d38ef9a91a04546e..760afe1c7f7a89aaa1723fbcd547871b41ab0603 100644 (file)
@@ -1289,7 +1289,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));
                }
 
@@ -7750,11 +7752,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;
index 5dd7c6470219e9afb87c9c7006f049192b6476da..ed0524ddff803c1dc891de60fea8ea73f349d28d 100644 (file)
@@ -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;