After a firmware crash mac80211 installs the same pairwise and group keys again. ath11k_install_key() leaves key_tsc_counter at zero, so the restarted firmware sends with PNs the key has already used: stations drop the frames as replays until the PN passes the last one they received, and CCMP and GCMP reuse the nonce under the same key. A station that had received 11, 2012 and 3512 frames before the crash accepted downlink again only after 11-13, 1817-2119 and 3324-3627 frames after the restart. Broadcast ARP requests are dropped as well, so a station whose neighbour entry expires stays unreachable. Start a reinstalled key past any PN it may have used. An HE PPDU carries at most 256 MPDUs and lasts at least 40 us, so add 8 PNs per microsecond since mac80211 last started the radio to the key's tx_pn. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Julius Bairaktaris --- RFC because WLAN.HK.2.9.0.1-02146 ignores the field: with key_tsc_counter set to 83834696 on every reinstalled key, the station that had received 3511 frames still needed 3324-3627 frames before it accepted downlink (1 run). Which command or field sets the TX PN of an installed key on this firmware? Without one the stations need new keys after a restart. drivers/net/wireless/ath/ath11k/core.h | 5 +++++ drivers/net/wireless/ath/ath11k/mac.c | 12 ++++++++++++ drivers/net/wireless/ath/ath11k/mac.h | 3 +++ drivers/net/wireless/ath/ath11k/wmi.c | 4 ++++ drivers/net/wireless/ath/ath11k/wmi.h | 1 + 5 files changed, 25 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index a0d725923ef24..683d6b750439c 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -668,6 +668,11 @@ struct ath11k { u8 mac_addr[ETH_ALEN]; struct ath11k_he ar_he; enum ath11k_state state; + /* last mac80211 start, and the TX PN advance for keys reinstalled + * after a firmware restart + */ + ktime_t start_time; + u64 restart_pn_advance; bool supports_6ghz; struct { struct completion started; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 1a237adaa37d1..0f9bf1d00546f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4421,6 +4421,13 @@ static int ath11k_install_key(struct ath11k_vif *arvif, key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV | IEEE80211_KEY_FLAG_RESERVE_TAILROOM; + /* The restarted firmware has lost the TX PN of a key that mac80211 + * installs again, so start it past any PN the key may have used. + */ + if (ar->state == ATH11K_STATE_RESTARTED) + arg.key_tsc_counter = atomic64_add_return(ar->restart_pn_advance, + &key->tx_pn); + install: ret = ath11k_wmi_vdev_install_key(arvif->ar, &arg); @@ -6645,6 +6652,9 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) break; case ATH11K_STATE_RESTARTING: ar->state = ATH11K_STATE_RESTARTED; + /* Every key in use was installed after the last start */ + ar->restart_pn_advance = ATH11K_MAX_MPDU_PER_US * + ktime_us_delta(ktime_get_boottime(), ar->start_time); ath11k_mac_wait_reconfigure(ab); break; case ATH11K_STATE_RESTARTED: @@ -6656,6 +6666,8 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) goto err; } + ar->start_time = ktime_get_boottime(); + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1, pdev->pdev_id); diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 127cb476b44b7..cde8f1c08eb0e 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -20,6 +20,9 @@ struct ath11k_generic_iter { }; /* number of failed packets (20 packets with 16 sw reties each) */ +/* An HE PPDU carries at most 256 MPDUs and lasts at least 40 us */ +#define ATH11K_MAX_MPDU_PER_US 8 + #define ATH11K_KICKOUT_THRESHOLD (20 * 16) /* Use insanely high numbers to make sure that the firmware implementation diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 5c27e7057e6ad..0deab00939af7 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1876,6 +1876,10 @@ int ath11k_wmi_vdev_install_key(struct ath11k *ar, memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, sizeof(struct wmi_key_seq_counter)); + if (arg->key_tsc_counter) + memcpy(&cmd->key_tsc_counter, &arg->key_tsc_counter, + sizeof(struct wmi_key_seq_counter)); + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | FIELD_PREP(WMI_TLV_LEN, key_len_aligned); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index b2dade0516ac7..049c90fa80c4d 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3690,6 +3690,7 @@ struct wmi_vdev_install_key_arg { u32 key_txmic_len; u32 key_rxmic_len; u64 key_rsc_counter; + u64 key_tsc_counter; const void *key_data; }; base-commit: cd026e68fdbf88b9cc522b76c7a9d92c9a41f709 prerequisite-patch-id: cd9f2849ade92e165f15fe3f887edabd1d06b6e8 prerequisite-patch-id: 3c9ba2aec0d93942448aba1dc5d26b632d519e5a prerequisite-patch-id: 9a833c602db147b490fba6df590eeea3847f4877 prerequisite-patch-id: 6fd3e11242fb3ead65751e3cd9972c4d7c63f989 prerequisite-patch-id: 01e149ed860e5e40e79725580548ce4034f344b2 -- 2.53.0