[PATCH ath-current] wifi: ath10k: fix station lookup failure during disconnect

Paul Menzel pmenzel at molgen.mpg.de
Fri Mar 27 02:27:20 PDT 2026


Dear Baochen,


Thank you for your reply.

Am 27.03.26 um 10:11 schrieb Baochen Qiang:

> On 3/27/2026 4:43 PM, Paul Menzel wrote:

>> Am 27.03.26 um 03:54 schrieb Baochen Qiang:
>>
>>> On 3/27/2026 7:31 AM, Paul Menzel wrote:
>>
>> […]
>>
>>>> Am 25.03.26 um 04:05 schrieb Baochen Qiang:
>>>>> Recent commit [1] moved station statistics collection to an earlier stage
>>>>> of the disconnect flow. With this change in place, ath10k fails to resolve
>>>>> the station entry when handling a peer stats event triggered during
>>>>> disconnect, resulting in log messages such as:
>>>>>
>>>>> wlp58s0: deauthenticating from 74:1a:e0:e7:b4:c8 by local choice (Reason: 3=DEAUTH_LEAVING)
>>>>> ath10k_pci 0000:3a:00.0: not found station for peer stats
>>>>> ath10k_pci 0000:3a:00.0: failed to parse stats info tlv: -22
>>>>>
>>>>> The failure occurs because ath10k relies on ieee80211_find_sta_by_ifaddr()
>>>>> for station lookup. That function uses local->sta_hash, but by the time
>>>>> the peer stats request is triggered during disconnect, mac80211 has
>>>>> already removed the station from that hash table, leading to lookup
>>>>> failure.
>>>>>
>>>>> Before commit [1], this issue was not visible because the transition from
>>>>> IEEE80211_STA_NONE to IEEE80211_STA_NOTEXIST prevented ath10k from sending
>>>>> a peer stats request at all: ath10k_mac_sta_get_peer_stats_info() would
>>>>> fail early to find the peer and skip requesting statistics.
>>>>>
>>>>> Fix this by switching the lookup path to ath10k_peer_find(), which queries
>>>>> ath10k's internal peer table. At the point where the firmware emits the
>>>>> peer stats event, the peer entry is still present in the driver's list,
>>>>> ensuring lookup succeeds.
>>>>
>>>> Out of curiosity, how can the statistics be printed?
>>>
>>> not quite understand your question, can you be more detailed?
>>
>> The commit message starts with:
>>
>> “… moved station statistics collection to an earlier stage …”
>>
>> I was wondering how to print/dump these statistics.
> 
> still not quite understand, but let me try to answer:
> 
> mac80211 collects stats and send it to userspace over nl80211,
> userpsace tools like iw can print it out.

Thank you. It looks like, one way is `sudo iw dev wlp58s0 station dump`.

>>>>> Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00309-QCARMSWPZ-1
>>>>>
>>>>> Fixes: a203dbeeca15 ("wifi: mac80211: collect station statistics earlier when disconnect") # [1]
>>>>> Reported-by: Paul Menzel <pmenzel at molgen.mpg.de>
>>>>> Closes: https://lore.kernel.org/ath10k/57671b89-ec9f-4e6c-992c-45eb8e75929c@molgen.mpg.de
>>>>> Signed-off-by: Baochen Qiang <baochen.qiang at oss.qualcomm.com>
>>>>> ---
>>>>>     drivers/net/wireless/ath/ath10k/wmi-tlv.c | 26 +++++++++++++++-----------
>>>>>     1 file changed, 15 insertions(+), 11 deletions(-)
>>>>>
>>>>> diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/
>>>>> ath10k/wmi-tlv.c
>>>>> index ec8e91707f84..01f2d1fa9d7d 100644
>>>>> --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
>>>>> +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
>>>>> @@ -3,7 +3,7 @@
>>>>>      * Copyright (c) 2005-2011 Atheros Communications Inc.
>>>>>      * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
>>>>>      * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
>>>>> - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
>>>>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>>>>>      */
>>>>>     #include "core.h"
>>>>>     #include "debug.h"
>>>>> @@ -14,6 +14,7 @@
>>>>>     #include "wmi-tlv.h"
>>>>>     #include "p2p.h"
>>>>>     #include "testmode.h"
>>>>> +#include "txrx.h"
>>>>>     #include <linux/bitfield.h>
>>>>>       /***************/
>>>>> @@ -224,8 +225,9 @@ static int ath10k_wmi_tlv_parse_peer_stats_info(struct ath10k *ar,
>>>>> u16 tag, u16
>>>>>                             const void *ptr, void *data)
>>>>>     {
>>>>>         const struct wmi_tlv_peer_stats_info *stat = ptr;
>>>>> -    struct ieee80211_sta *sta;
>>>>> +    u32 vdev_id = *(u32 *)data;
>>>>>         struct ath10k_sta *arsta;
>>>>> +    struct ath10k_peer *peer;
>>>>>           if (tag != WMI_TLV_TAG_STRUCT_PEER_STATS_INFO)
>>>>>             return -EPROTO;
>>>>> @@ -241,20 +243,20 @@ static int ath10k_wmi_tlv_parse_peer_stats_info(struct ath10k
>>>>> *ar, u16 tag, u16
>>>>>                __le32_to_cpu(stat->last_tx_rate_code),
>>>>>                __le32_to_cpu(stat->last_tx_bitrate_kbps));
>>>>> -    rcu_read_lock();
>>>>> -    sta = ieee80211_find_sta_by_ifaddr(ar->hw, stat->peer_macaddr.addr, NULL);
>>>>> -    if (!sta) {
>>>>> -        rcu_read_unlock();
>>>>> -        ath10k_warn(ar, "not found station for peer stats\n");
>>>>> +    guard(spinlock_bh)(&ar->data_lock);
>>>>> +
>>>>> +    peer = ath10k_peer_find(ar, vdev_id, stat->peer_macaddr.addr);
>>>>> +    if (!peer || !peer->sta) {
>>>>> +        ath10k_warn(ar, "not found %s with vdev id %u mac addr %pM for peer stats\n",
>>>>> +                peer ? "sta" : "peer", vdev_id, stat->peer_macaddr.addr);
>>>>>             return -EINVAL;
>>>>>         }
>>>>> -    arsta = (struct ath10k_sta *)sta->drv_priv;
>>>>> +    arsta = (struct ath10k_sta *)peer->sta->drv_priv;
>>>>>         arsta->rx_rate_code = __le32_to_cpu(stat->last_rx_rate_code);
>>>>>         arsta->rx_bitrate_kbps = __le32_to_cpu(stat->last_rx_bitrate_kbps);
>>>>>         arsta->tx_rate_code = __le32_to_cpu(stat->last_tx_rate_code);
>>>>>         arsta->tx_bitrate_kbps = __le32_to_cpu(stat->last_tx_bitrate_kbps);
>>>>> -      rcu_read_unlock();
>>>>>           return 0;
>>>>>     }
>>>>> @@ -266,6 +268,7 @@ static int ath10k_wmi_tlv_op_pull_peer_stats_info(struct ath10k *ar,
>>>>>         const struct wmi_tlv_peer_stats_info_ev *ev;
>>>>>         const void *data;
>>>>>         u32 num_peer_stats;
>>>>> +      u32 vdev_id;
>>>>>         int ret;
>>>>>           tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
>>>>> @@ -284,15 +287,16 @@ static int ath10k_wmi_tlv_op_pull_peer_stats_info(struct ath10k *ar,
>>>>>         }
>>>>>           num_peer_stats = __le32_to_cpu(ev->num_peers);
>>>>> +        vdev_id = __le32_to_cpu(ev->vdev_id);
>>>>>           ath10k_dbg(ar, ATH10K_DBG_WMI,
>>>>>                "wmi tlv peer stats info update peer vdev id %d peers %i more data %d\n",
>>>>> -           __le32_to_cpu(ev->vdev_id),
>>>>> +           vdev_id,
>>>>>                num_peer_stats,
>>>>>                __le32_to_cpu(ev->more_data));
>>>>>           ret = ath10k_wmi_tlv_iter(ar, data, ath10k_wmi_tlv_len(data),
>>>>> -                  ath10k_wmi_tlv_parse_peer_stats_info, NULL);
>>>>> +                  ath10k_wmi_tlv_parse_peer_stats_info, &vdev_id);
>>>>>         if (ret)
>>>>>             ath10k_warn(ar, "failed to parse stats info tlv: %d\n", ret);
>>>>
>>>> Reviewed-by: Paul Menzel <pmenzel at molgen.mpg.de>
>>>> Tested-by: Paul Menzel <pmenzel at molgen.mpg.de>


Kind regards,

Paul


PS: For the record:

```
$ sudo iw dev wlp58s0 station dump
Station 8c:6a:8d:xx:xx:xx (on wlp58s0)
	authorized:	yes
	authenticated:	yes
	associated:	yes
	preamble:	long
	WMM/WME:	yes
	MFP:		no
	TDLS peer:	no
	inactive time:	136 ms
	rx bytes:	42923460
	rx packets:	30379
	tx bytes:	527970
	tx packets:	4543
	tx retries:	0
	tx failed:	0
	beacon loss:	0
		beacon rx:	254
	rx drop misc:	104
	signal:  	-35 [-43, -39] dBm
	signal avg:	-35 [-44, -37] dBm
	beacon signal avg:	-35 dBm
	tx bitrate:	144.4 MBit/s MCS 15 short GI
	tx duration:	1386481 us
	rx bitrate:	144.4 MBit/s MCS 15 short GI
	rx duration:	0 us
	airtime weight: 256
	DTIM period:	1
	beacon interval:100
	short slot time:yes
	connected time:	61 seconds
	associated at [boottime]:	6663.920s
	associated at:	1774603392069 ms
	current time:	1774603452468 ms
```



More information about the ath10k mailing list