]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: Refactor sta state machine
authorSriram R <quic_srirrama@quicinc.com>
Fri, 1 Nov 2024 15:17:00 +0000 (17:17 +0200)
committerJeff Johnson <quic_jjohnson@quicinc.com>
Wed, 6 Nov 2024 19:36:28 +0000 (11:36 -0800)
Refactor ath12k_mac_op_sta_state(), with generic wrappers which can be used for
both multi link stations and non-ML stations.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
Signed-off-by: Harshitha Prem <quic_hprem@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Link: https://patch.msgid.link/20241101151705.165987-4-kvalo@kernel.org
Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c

index 3bf31ee5b9fa4722a54e2b866c1ce0eb049eef15..23e27d11985955e55535f3c7c45e8c69a5edb2ec 100644 (file)
@@ -469,6 +469,9 @@ struct ath12k_link_sta {
        struct ath12k_link_vif *arvif;
        struct ath12k_sta *ahsta;
 
+       /* link address similar to ieee80211_link_sta */
+       u8 addr[ETH_ALEN];
+
        /* the following are protected by ar->data_lock */
        u32 changed; /* IEEE80211_RC_* */
        u32 bw;
index 1a97eafaa3d243c9781b22bc5c307af423dfad85..73a80b55b229f6b3d1a222690e203f2a7106f927 100644 (file)
@@ -4505,10 +4505,10 @@ ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_link_vif *arvif,
        return ret;
 }
 
-static int ath12k_station_assoc(struct ath12k *ar,
-                               struct ath12k_link_vif *arvif,
-                               struct ath12k_link_sta *arsta,
-                               bool reassoc)
+static int ath12k_mac_station_assoc(struct ath12k *ar,
+                                   struct ath12k_link_vif *arvif,
+                                   struct ath12k_link_sta *arsta,
+                                   bool reassoc)
 {
        struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
        struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
@@ -4595,28 +4595,19 @@ static int ath12k_station_assoc(struct ath12k *ar,
        return 0;
 }
 
-static int ath12k_station_disassoc(struct ath12k *ar,
-                                  struct ath12k_link_vif *arvif,
-                                  struct ath12k_link_sta *arsta)
+static int ath12k_mac_station_disassoc(struct ath12k *ar,
+                                      struct ath12k_link_vif *arvif,
+                                      struct ath12k_link_sta *arsta)
 {
        struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
-       int ret;
 
        lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
 
        if (!sta->wme) {
                arvif->num_legacy_stations--;
-               ret = ath12k_recalc_rtscts_prot(arvif);
-               if (ret)
-                       return ret;
+               return ath12k_recalc_rtscts_prot(arvif);
        }
 
-       ret = ath12k_clear_peer_keys(arvif, sta->addr);
-       if (ret) {
-               ath12k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n",
-                           arvif->vdev_id, ret);
-               return ret;
-       }
        return 0;
 }
 
@@ -4812,6 +4803,147 @@ static void ath12k_mac_dec_num_stations(struct ath12k_link_vif *arvif,
        ar->num_stations--;
 }
 
+static void ath12k_mac_station_post_remove(struct ath12k *ar,
+                                          struct ath12k_link_vif *arvif,
+                                          struct ath12k_link_sta *arsta)
+{
+       struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+       struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+       struct ath12k_sta *ahsta = arsta->ahsta;
+       struct ath12k_peer *peer;
+
+       lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+       ath12k_mac_dec_num_stations(arvif, arsta);
+
+       spin_lock_bh(&ar->ab->base_lock);
+
+       peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
+       if (peer && peer->sta == sta) {
+               ath12k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
+                           vif->addr, arvif->vdev_id);
+               peer->sta = NULL;
+               list_del(&peer->list);
+               kfree(peer);
+               ar->num_peers--;
+       }
+
+       spin_unlock_bh(&ar->ab->base_lock);
+
+       kfree(arsta->rx_stats);
+       arsta->rx_stats = NULL;
+
+       if (arsta->link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
+               ahsta->links_map &= ~(BIT(arsta->link_id));
+               rcu_assign_pointer(ahsta->link[arsta->link_id], NULL);
+               synchronize_rcu();
+               arsta->link_id = ATH12K_INVALID_LINK_ID;
+               arsta->ahsta = NULL;
+       }
+}
+
+static int ath12k_mac_station_unauthorize(struct ath12k *ar,
+                                         struct ath12k_link_vif *arvif,
+                                         struct ath12k_link_sta *arsta)
+{
+       struct ath12k_peer *peer;
+       int ret;
+
+       lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+       spin_lock_bh(&ar->ab->base_lock);
+
+       peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr);
+       if (peer)
+               peer->is_authorized = false;
+
+       spin_unlock_bh(&ar->ab->base_lock);
+
+       /* Driver must clear the keys during the state change from
+        * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC, since after
+        * returning from here, mac80211 is going to delete the keys
+        * in __sta_info_destroy_part2(). This will ensure that the driver does
+        * not retain stale key references after mac80211 deletes the keys.
+        */
+       ret = ath12k_clear_peer_keys(arvif, arsta->addr);
+       if (ret) {
+               ath12k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath12k_mac_station_authorize(struct ath12k *ar,
+                                       struct ath12k_link_vif *arvif,
+                                       struct ath12k_link_sta *arsta)
+{
+       struct ath12k_peer *peer;
+       struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+       struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+       int ret;
+
+       lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+       spin_lock_bh(&ar->ab->base_lock);
+
+       peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
+       if (peer)
+               peer->is_authorized = true;
+
+       spin_unlock_bh(&ar->ab->base_lock);
+
+       if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) {
+               ret = ath12k_wmi_set_peer_param(ar, sta->addr,
+                                               arvif->vdev_id,
+                                               WMI_PEER_AUTHORIZE,
+                                               1);
+               if (ret) {
+                       ath12k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n",
+                                   sta->addr, arvif->vdev_id, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath12k_mac_station_remove(struct ath12k *ar,
+                                    struct ath12k_link_vif *arvif,
+                                    struct ath12k_link_sta *arsta)
+{
+       struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+       struct ath12k_vif *ahvif = arvif->ahvif;
+       int ret;
+
+       lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+       wiphy_work_cancel(ar->ah->hw->wiphy, &arsta->update_wk);
+
+       if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) {
+               ath12k_bss_disassoc(ar, arvif);
+               ret = ath12k_mac_vdev_stop(arvif);
+               if (ret)
+                       ath12k_warn(ar->ab, "failed to stop vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+       }
+
+       ath12k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
+
+       ret = ath12k_peer_delete(ar, arvif->vdev_id, sta->addr);
+       if (ret)
+               ath12k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n",
+                           sta->addr, arvif->vdev_id);
+       else
+               ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n",
+                          sta->addr, arvif->vdev_id);
+
+       ath12k_mac_station_post_remove(ar, arvif, arsta);
+
+       return ret;
+}
+
 static int ath12k_mac_station_add(struct ath12k *ar,
                                  struct ath12k_link_vif *arvif,
                                  struct ath12k_link_sta *arsta)
@@ -4919,31 +5051,37 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
        return bw;
 }
 
-static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif,
-                                  struct ieee80211_sta *sta,
-                                  enum ieee80211_sta_state old_state,
-                                  enum ieee80211_sta_state new_state)
+static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
+                                           struct ath12k_link_vif *arvif,
+                                           struct ath12k_link_sta *arsta,
+                                           enum ieee80211_sta_state old_state,
+                                           enum ieee80211_sta_state new_state)
 {
-       struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
-       struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);
-       struct ath12k *ar;
-       struct ath12k_link_vif *arvif;
-       struct ath12k_link_sta *arsta;
-       struct ath12k_peer *peer;
+       struct ath12k *ar = arvif->ar;
+       struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+       struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+       struct ath12k_sta *ahsta = arsta->ahsta;
        int ret = 0;
 
        lockdep_assert_wiphy(hw->wiphy);
 
-       arvif = &ahvif->deflink;
-       arsta = &ahsta->deflink;
+       /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: Remove the station
+        * from driver
+        */
+       if ((old_state == IEEE80211_STA_NONE &&
+            new_state == IEEE80211_STA_NOTEXIST)) {
+               /* ML sta needs separate handling */
+               if (sta->mlo)
+                       return 0;
 
-       ar = ath12k_get_ar_by_vif(hw, vif);
-       if (!ar) {
-               WARN_ON_ONCE(1);
-               return -EINVAL;
+               ret = ath12k_mac_station_remove(ar, arvif, arsta);
+               if (ret) {
+                       ath12k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n",
+                                   arsta->addr, arvif->vdev_id);
+               }
        }
 
+       /* IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE: Add new station to driver */
        if (old_state == IEEE80211_STA_NOTEXIST &&
            new_state == IEEE80211_STA_NONE) {
                memset(arsta, 0, sizeof(*arsta));
@@ -4961,56 +5099,16 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
                if (ret)
                        ath12k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
                                    sta->addr, arvif->vdev_id);
-       } else if ((old_state == IEEE80211_STA_NONE &&
-                   new_state == IEEE80211_STA_NOTEXIST)) {
-               wiphy_work_cancel(hw->wiphy, &arsta->update_wk);
 
-               if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) {
-                       ath12k_bss_disassoc(ar, arvif);
-                       ret = ath12k_mac_vdev_stop(arvif);
-                       if (ret)
-                               ath12k_warn(ar->ab, "failed to stop vdev %i: %d\n",
-                                           arvif->vdev_id, ret);
-               }
-               ath12k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
-
-               ret = ath12k_peer_delete(ar, arvif->vdev_id, sta->addr);
-               if (ret)
-                       ath12k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n",
-                                   sta->addr, arvif->vdev_id);
-               else
-                       ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n",
-                                  sta->addr, arvif->vdev_id);
-
-               ath12k_mac_dec_num_stations(arvif, arsta);
-               spin_lock_bh(&ar->ab->base_lock);
-               peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (peer && peer->sta == sta) {
-                       ath12k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
-                                   vif->addr, arvif->vdev_id);
-                       peer->sta = NULL;
-                       list_del(&peer->list);
-                       kfree(peer);
-                       ar->num_peers--;
-               }
-               spin_unlock_bh(&ar->ab->base_lock);
-
-               kfree(arsta->rx_stats);
-               arsta->rx_stats = NULL;
-
-               if (arsta->link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
-                       rcu_assign_pointer(ahsta->link[arsta->link_id], NULL);
-                       synchronize_rcu();
-                       ahsta->links_map &= ~(BIT(arsta->link_id));
-                       arsta->link_id = ATH12K_INVALID_LINK_ID;
-                       arsta->ahsta = NULL;
-               }
+       /* IEEE80211_STA_AUTH -> IEEE80211_STA_ASSOC: Send station assoc command for
+        * peer associated to AP/Mesh/ADHOC vif type.
+        */
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC &&
                   (vif->type == NL80211_IFTYPE_AP ||
                    vif->type == NL80211_IFTYPE_MESH_POINT ||
                    vif->type == NL80211_IFTYPE_ADHOC)) {
-               ret = ath12k_station_assoc(ar, arvif, arsta, false);
+               ret = ath12k_mac_station_assoc(ar, arvif, arsta, false);
                if (ret)
                        ath12k_warn(ar->ab, "Failed to associate station: %pM\n",
                                    sta->addr);
@@ -5021,40 +5119,32 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
                arsta->bw_prev = sta->deflink.bandwidth;
 
                spin_unlock_bh(&ar->data_lock);
+
+       /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as
+        * authorized
+        */
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
-               spin_lock_bh(&ar->ab->base_lock);
-
-               peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (peer)
-                       peer->is_authorized = true;
-
-               spin_unlock_bh(&ar->ab->base_lock);
+               ret = ath12k_mac_station_authorize(ar, arvif, arsta);
+               if (ret)
+                       ath12k_warn(ar->ab, "Failed to authorize station: %pM\n",
+                                   sta->addr);
 
-               if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) {
-                       ret = ath12k_wmi_set_peer_param(ar, sta->addr,
-                                                       arvif->vdev_id,
-                                                       WMI_PEER_AUTHORIZE,
-                                                       1);
-                       if (ret)
-                               ath12k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n",
-                                           sta->addr, arvif->vdev_id, ret);
-               }
+       /* IEEE80211_STA_AUTHORIZED -> IEEE80211_STA_ASSOC: station may be in removal,
+        * deauthorize it.
+        */
        } else if (old_state == IEEE80211_STA_AUTHORIZED &&
                   new_state == IEEE80211_STA_ASSOC) {
-               spin_lock_bh(&ar->ab->base_lock);
-
-               peer = ath12k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (peer)
-                       peer->is_authorized = false;
-
-               spin_unlock_bh(&ar->ab->base_lock);
+               ath12k_mac_station_unauthorize(ar, arvif, arsta);
+       /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTH: disassoc peer connected to
+        * AP/mesh/ADHOC vif type.
+        */
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
                   (vif->type == NL80211_IFTYPE_AP ||
                    vif->type == NL80211_IFTYPE_MESH_POINT ||
                    vif->type == NL80211_IFTYPE_ADHOC)) {
-               ret = ath12k_station_disassoc(ar, arvif, arsta);
+               ret = ath12k_mac_station_disassoc(ar, arvif, arsta);
                if (ret)
                        ath12k_warn(ar->ab, "Failed to disassociate station: %pM\n",
                                    sta->addr);
@@ -5063,6 +5153,55 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
        return ret;
 }
 
+static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sta *sta,
+                                  enum ieee80211_sta_state old_state,
+                                  enum ieee80211_sta_state new_state)
+{
+       struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
+       struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);
+       struct ath12k_link_vif *arvif;
+       struct ath12k_link_sta *arsta;
+       int ret;
+       u8 link_id = 0;
+
+       lockdep_assert_wiphy(hw->wiphy);
+
+       if (ieee80211_vif_is_mld(vif) && sta->valid_links) {
+               WARN_ON(!sta->mlo && hweight16(sta->valid_links) != 1);
+               link_id = ffs(sta->valid_links) - 1;
+       }
+
+       /* Handle for non-ML station */
+       if (!sta->mlo) {
+               arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
+               arsta = &ahsta->deflink;
+               arsta->ahsta = ahsta;
+
+               if (WARN_ON(!arvif || !arsta)) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+
+               /* vdev might be in deleted */
+               if (WARN_ON(!arvif->ar)) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+
+               ret = ath12k_mac_handle_link_sta_state(hw, arvif, arsta,
+                                                      old_state, new_state);
+               if (ret)
+                       goto exit;
+       }
+
+       ret = 0;
+
+exit:
+       return ret;
+}
+
 static int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct ieee80211_sta *sta)