@@ -4317,6 +4317,40 @@ static int ath11k_clear_peer_keys(struct ath11k_vif *arvif,
43174317 return first_errno ;
43184318}
43194319
4320+ static int ath11k_set_group_keys (struct ath11k_vif * arvif )
4321+ {
4322+ struct ath11k * ar = arvif -> ar ;
4323+ struct ath11k_base * ab = ar -> ab ;
4324+ const u8 * addr = arvif -> bssid ;
4325+ int i , ret , first_errno = 0 ;
4326+ struct ath11k_peer * peer ;
4327+
4328+ spin_lock_bh (& ab -> base_lock );
4329+ peer = ath11k_peer_find (ab , arvif -> vdev_id , addr );
4330+ spin_unlock_bh (& ab -> base_lock );
4331+
4332+ if (!peer )
4333+ return - ENOENT ;
4334+
4335+ for (i = 0 ; i < ARRAY_SIZE (peer -> keys ); i ++ ) {
4336+ struct ieee80211_key_conf * key = peer -> keys [i ];
4337+
4338+ if (!key || (key -> flags & IEEE80211_KEY_FLAG_PAIRWISE ))
4339+ continue ;
4340+
4341+ ret = ath11k_install_key (arvif , key , SET_KEY , addr ,
4342+ WMI_KEY_GROUP );
4343+ if (ret < 0 && first_errno == 0 )
4344+ first_errno = ret ;
4345+
4346+ if (ret < 0 )
4347+ ath11k_warn (ab , "failed to set group key of idx %d for vdev %d: %d\n" ,
4348+ i , arvif -> vdev_id , ret );
4349+ }
4350+
4351+ return first_errno ;
4352+ }
4353+
43204354static int ath11k_mac_op_set_key (struct ieee80211_hw * hw , enum set_key_cmd cmd ,
43214355 struct ieee80211_vif * vif , struct ieee80211_sta * sta ,
43224356 struct ieee80211_key_conf * key )
@@ -4326,6 +4360,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
43264360 struct ath11k_vif * arvif = ath11k_vif_to_arvif (vif );
43274361 struct ath11k_peer * peer ;
43284362 struct ath11k_sta * arsta ;
4363+ bool is_ap_with_no_sta ;
43294364 const u8 * peer_addr ;
43304365 int ret = 0 ;
43314366 u32 flags = 0 ;
@@ -4386,16 +4421,57 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
43864421 else
43874422 flags |= WMI_KEY_GROUP ;
43884423
4389- ret = ath11k_install_key (arvif , key , cmd , peer_addr , flags );
4390- if (ret ) {
4391- ath11k_warn (ab , "ath11k_install_key failed (%d)\n" , ret );
4392- goto exit ;
4393- }
4424+ ath11k_dbg (ar -> ab , ATH11K_DBG_MAC ,
4425+ "%s for peer %pM on vdev %d flags 0x%X, type = %d, num_sta %d\n" ,
4426+ cmd == SET_KEY ? "SET_KEY" : "DEL_KEY" , peer_addr , arvif -> vdev_id ,
4427+ flags , arvif -> vdev_type , arvif -> num_stations );
4428+
4429+ /* Allow group key clearing only in AP mode when no stations are
4430+ * associated. There is a known race condition in firmware where
4431+ * group addressed packets may be dropped if the key is cleared
4432+ * and immediately set again during rekey.
4433+ *
4434+ * During GTK rekey, mac80211 issues a clear key (if the old key
4435+ * exists) followed by an install key operation for same key
4436+ * index. This causes ath11k to send two WMI commands in quick
4437+ * succession: one to clear the old key and another to install the
4438+ * new key in the same slot.
4439+ *
4440+ * Under certain conditions—especially under high load or time
4441+ * sensitive scenarios, firmware may process these commands
4442+ * asynchronously in a way that firmware assumes the key is
4443+ * cleared whereas hardware has a valid key. This inconsistency
4444+ * between hardware and firmware leads to group addressed packet
4445+ * drops after rekey.
4446+ * Only setting the same key again can restore a valid key in
4447+ * firmware and allow packets to be transmitted.
4448+ *
4449+ * There is a use case where an AP can transition from Secure mode
4450+ * to open mode without a vdev restart by just deleting all
4451+ * associated peers and clearing key, Hence allow clear key for
4452+ * that case alone. Mark arvif->reinstall_group_keys in such cases
4453+ * and reinstall the same key when the first peer is added,
4454+ * allowing firmware to recover from the race if it had occurred.
4455+ */
43944456
4395- ret = ath11k_dp_peer_rx_pn_replay_config (arvif , peer_addr , cmd , key );
4396- if (ret ) {
4397- ath11k_warn (ab , "failed to offload PN replay detection %d\n" , ret );
4398- goto exit ;
4457+ is_ap_with_no_sta = (vif -> type == NL80211_IFTYPE_AP &&
4458+ !arvif -> num_stations );
4459+ if ((flags & WMI_KEY_PAIRWISE ) || cmd == SET_KEY || is_ap_with_no_sta ) {
4460+ ret = ath11k_install_key (arvif , key , cmd , peer_addr , flags );
4461+ if (ret ) {
4462+ ath11k_warn (ab , "ath11k_install_key failed (%d)\n" , ret );
4463+ goto exit ;
4464+ }
4465+
4466+ ret = ath11k_dp_peer_rx_pn_replay_config (arvif , peer_addr , cmd , key );
4467+ if (ret ) {
4468+ ath11k_warn (ab , "failed to offload PN replay detection %d\n" ,
4469+ ret );
4470+ goto exit ;
4471+ }
4472+
4473+ if ((flags & WMI_KEY_GROUP ) && cmd == SET_KEY && is_ap_with_no_sta )
4474+ arvif -> reinstall_group_keys = true;
43994475 }
44004476
44014477 spin_lock_bh (& ab -> base_lock );
@@ -4994,6 +5070,7 @@ static int ath11k_mac_inc_num_stations(struct ath11k_vif *arvif,
49945070 return - ENOBUFS ;
49955071
49965072 ar -> num_stations ++ ;
5073+ arvif -> num_stations ++ ;
49975074
49985075 return 0 ;
49995076}
@@ -5009,6 +5086,7 @@ static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif,
50095086 return ;
50105087
50115088 ar -> num_stations -- ;
5089+ arvif -> num_stations -- ;
50125090}
50135091
50145092static u32 ath11k_mac_ieee80211_sta_bw_to_wmi (struct ath11k * ar ,
@@ -9540,6 +9618,21 @@ static int ath11k_mac_station_add(struct ath11k *ar,
95409618 goto exit ;
95419619 }
95429620
9621+ /* Driver allows the DEL KEY followed by SET KEY sequence for
9622+ * group keys for only when there is no clients associated, if at
9623+ * all firmware has entered the race during that window,
9624+ * reinstalling the same key when the first sta connects will allow
9625+ * firmware to recover from the race.
9626+ */
9627+ if (arvif -> num_stations == 1 && arvif -> reinstall_group_keys ) {
9628+ ath11k_dbg (ab , ATH11K_DBG_MAC , "set group keys on 1st station add for vdev %d\n" ,
9629+ arvif -> vdev_id );
9630+ ret = ath11k_set_group_keys (arvif );
9631+ if (ret )
9632+ goto dec_num_station ;
9633+ arvif -> reinstall_group_keys = false;
9634+ }
9635+
95439636 arsta -> rx_stats = kzalloc (sizeof (* arsta -> rx_stats ), GFP_KERNEL );
95449637 if (!arsta -> rx_stats ) {
95459638 ret = - ENOMEM ;
0 commit comments