Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a49ae94 authored by Mohammed Shafi Shajakhan's avatar Mohammed Shafi Shajakhan Committed by Kalle Valo
Browse files

ath10k: fix 10.4 extended peer stats update



10.4 'extended peer stats' will be not be appended with normal peer stats
data and they shall be coming in separate chunks. Fix this by maintaining
a separate linked list 'extender peer stats' for 10.4 and update
rx_duration for per station statistics. Also parse through beacon filter
(if enabled), to make sure we parse the extended peer stats properly.
This issue was exposed when more than one client is connected and
extended peer stats for 10.4 is enabled

The order for the stats is as below
S - standard peer stats, E- extended peer stats, B - beacon filter stats

{S1, S2, S3..} -> {B1, B2, B3..}(if available) -> {E1, E2, E3..}

Fixes: f9575793 ("ath10k: enable parsing per station rx duration for 10.4")
Signed-off-by: default avatarMohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent aaab50fc
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -165,6 +165,13 @@ struct ath10k_fw_stats_peer {
	u32 rx_duration;
	u32 rx_duration;
};
};


struct ath10k_fw_extd_stats_peer {
	struct list_head list;

	u8 peer_macaddr[ETH_ALEN];
	u32 rx_duration;
};

struct ath10k_fw_stats_vdev {
struct ath10k_fw_stats_vdev {
	struct list_head list;
	struct list_head list;


@@ -256,9 +263,11 @@ struct ath10k_fw_stats_pdev {
};
};


struct ath10k_fw_stats {
struct ath10k_fw_stats {
	bool extended;
	struct list_head pdevs;
	struct list_head pdevs;
	struct list_head vdevs;
	struct list_head vdevs;
	struct list_head peers;
	struct list_head peers;
	struct list_head peers_extd;
};
};


#define ATH10K_TPC_TABLE_TYPE_FLAG	1
#define ATH10K_TPC_TABLE_TYPE_FLAG	1
+18 −1
Original line number Original line Diff line number Diff line
@@ -313,13 +313,25 @@ static void ath10k_fw_stats_peers_free(struct list_head *head)
	}
	}
}
}


static void ath10k_fw_extd_stats_peers_free(struct list_head *head)
{
	struct ath10k_fw_extd_stats_peer *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
{
{
	spin_lock_bh(&ar->data_lock);
	spin_lock_bh(&ar->data_lock);
	ar->debug.fw_stats_done = false;
	ar->debug.fw_stats_done = false;
	ar->debug.fw_stats.extended = false;
	ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
	ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
	ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
	ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
	ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
	ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
	ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);
	spin_unlock_bh(&ar->data_lock);
	spin_unlock_bh(&ar->data_lock);
}
}


@@ -334,6 +346,7 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
	INIT_LIST_HEAD(&stats.pdevs);
	INIT_LIST_HEAD(&stats.pdevs);
	INIT_LIST_HEAD(&stats.vdevs);
	INIT_LIST_HEAD(&stats.vdevs);
	INIT_LIST_HEAD(&stats.peers);
	INIT_LIST_HEAD(&stats.peers);
	INIT_LIST_HEAD(&stats.peers_extd);


	spin_lock_bh(&ar->data_lock);
	spin_lock_bh(&ar->data_lock);
	ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
	ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
@@ -354,7 +367,7 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
	 *     delivered which is treated as end-of-data and is itself discarded
	 *     delivered which is treated as end-of-data and is itself discarded
	 */
	 */
	if (ath10k_peer_stats_enabled(ar))
	if (ath10k_peer_stats_enabled(ar))
		ath10k_sta_update_rx_duration(ar, &stats.peers);
		ath10k_sta_update_rx_duration(ar, &stats);


	if (ar->debug.fw_stats_done) {
	if (ar->debug.fw_stats_done) {
		if (!ath10k_peer_stats_enabled(ar))
		if (!ath10k_peer_stats_enabled(ar))
@@ -396,6 +409,8 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)


		list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
		list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
		list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
		list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
		list_splice_tail_init(&stats.peers_extd,
				      &ar->debug.fw_stats.peers_extd);
	}
	}


	complete(&ar->debug.fw_stats_complete);
	complete(&ar->debug.fw_stats_complete);
@@ -407,6 +422,7 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
	ath10k_fw_stats_pdevs_free(&stats.pdevs);
	ath10k_fw_stats_pdevs_free(&stats.pdevs);
	ath10k_fw_stats_vdevs_free(&stats.vdevs);
	ath10k_fw_stats_vdevs_free(&stats.vdevs);
	ath10k_fw_stats_peers_free(&stats.peers);
	ath10k_fw_stats_peers_free(&stats.peers);
	ath10k_fw_extd_stats_peers_free(&stats.peers_extd);


	spin_unlock_bh(&ar->data_lock);
	spin_unlock_bh(&ar->data_lock);
}
}
@@ -2330,6 +2346,7 @@ int ath10k_debug_create(struct ath10k *ar)
	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
	INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
	INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
	INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
	INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
	INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);


	return 0;
	return 0;
}
}
+5 −3
Original line number Original line Diff line number Diff line
@@ -154,10 +154,12 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
#ifdef CONFIG_MAC80211_DEBUGFS
#ifdef CONFIG_MAC80211_DEBUGFS
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			    struct ieee80211_sta *sta, struct dentry *dir);
			    struct ieee80211_sta *sta, struct dentry *dir);
void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *peer);
void ath10k_sta_update_rx_duration(struct ath10k *ar,
				   struct ath10k_fw_stats *stats);
#else
#else
static inline void ath10k_sta_update_rx_duration(struct ath10k *ar,
static inline
						 struct list_head *peer)
void ath10k_sta_update_rx_duration(struct ath10k *ar,
				   struct ath10k_fw_stats *stats)
{
{
}
}
#endif /* CONFIG_MAC80211_DEBUGFS */
#endif /* CONFIG_MAC80211_DEBUGFS */
+33 −3
Original line number Original line Diff line number Diff line
@@ -18,13 +18,34 @@
#include "wmi-ops.h"
#include "wmi-ops.h"
#include "debug.h"
#include "debug.h"


void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *head)
static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar,
{	struct ieee80211_sta *sta;
						     struct ath10k_fw_stats *stats)
{
	struct ath10k_fw_extd_stats_peer *peer;
	struct ieee80211_sta *sta;
	struct ath10k_sta *arsta;

	rcu_read_lock();
	list_for_each_entry(peer, &stats->peers_extd, list) {
		sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,
						   NULL);
		if (!sta)
			continue;
		arsta = (struct ath10k_sta *)sta->drv_priv;
		arsta->rx_duration += (u64)peer->rx_duration;
	}
	rcu_read_unlock();
}

static void ath10k_sta_update_stats_rx_duration(struct ath10k *ar,
						struct ath10k_fw_stats *stats)
{
	struct ath10k_fw_stats_peer *peer;
	struct ath10k_fw_stats_peer *peer;
	struct ieee80211_sta *sta;
	struct ath10k_sta *arsta;
	struct ath10k_sta *arsta;


	rcu_read_lock();
	rcu_read_lock();
	list_for_each_entry(peer, head, list) {
	list_for_each_entry(peer, &stats->peers, list) {
		sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,
		sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,
						   NULL);
						   NULL);
		if (!sta)
		if (!sta)
@@ -35,6 +56,15 @@ void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *head)
	rcu_read_unlock();
	rcu_read_unlock();
}
}


void ath10k_sta_update_rx_duration(struct ath10k *ar,
				   struct ath10k_fw_stats *stats)
{
	if (stats->extended)
		ath10k_sta_update_extd_stats_rx_duration(ar, stats);
	else
		ath10k_sta_update_stats_rx_duration(ar, stats);
}

static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
					     char __user *user_buf,
					     char __user *user_buf,
					     size_t count, loff_t *ppos)
					     size_t count, loff_t *ppos)
+41 −14
Original line number Original line Diff line number Diff line
@@ -2926,6 +2926,7 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
	u32 num_pdev_ext_stats;
	u32 num_pdev_ext_stats;
	u32 num_vdev_stats;
	u32 num_vdev_stats;
	u32 num_peer_stats;
	u32 num_peer_stats;
	u32 num_bcnflt_stats;
	u32 stats_id;
	u32 stats_id;
	int i;
	int i;


@@ -2936,6 +2937,7 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
	num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
	num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
	num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
	num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
	num_bcnflt_stats = __le32_to_cpu(ev->num_bcnflt_stats);
	stats_id = __le32_to_cpu(ev->stats_id);
	stats_id = __le32_to_cpu(ev->stats_id);


	for (i = 0; i < num_pdev_stats; i++) {
	for (i = 0; i < num_pdev_stats; i++) {
@@ -2976,30 +2978,55 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
	/* fw doesn't implement vdev stats */
	/* fw doesn't implement vdev stats */


	for (i = 0; i < num_peer_stats; i++) {
	for (i = 0; i < num_peer_stats; i++) {
		const struct wmi_10_4_peer_extd_stats *src;
		const struct wmi_10_4_peer_stats *src;
		struct ath10k_fw_stats_peer *dst;
		struct ath10k_fw_stats_peer *dst;
		int stats_len;
		bool extd_peer_stats = !!(stats_id & WMI_10_4_STAT_PEER_EXTD);


		if (extd_peer_stats)
		src = (void *)skb->data;
			stats_len = sizeof(struct wmi_10_4_peer_extd_stats);
		if (!skb_pull(skb, sizeof(*src)))
		else
			return -EPROTO;
			stats_len = sizeof(struct wmi_10_4_peer_stats);

		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
		if (!dst)
			continue;

		ath10k_wmi_10_4_pull_peer_stats(src, dst);
		list_add_tail(&dst->list, &stats->peers);
	}

	for (i = 0; i < num_bcnflt_stats; i++) {
		const struct wmi_10_4_bss_bcn_filter_stats *src;


		src = (void *)skb->data;
		src = (void *)skb->data;
		if (!skb_pull(skb, stats_len))
		if (!skb_pull(skb, sizeof(*src)))
			return -EPROTO;

		/* FIXME: expose values to userspace
		 *
		 * Note: Even though this loop seems to do nothing it is
		 * required to parse following sub-structures properly.
		 */
	}

	if ((stats_id & WMI_10_4_STAT_PEER_EXTD) == 0)
		return 0;

	stats->extended = true;

	for (i = 0; i < num_peer_stats; i++) {
		const struct wmi_10_4_peer_extd_stats *src;
		struct ath10k_fw_extd_stats_peer *dst;

		src = (void *)skb->data;
		if (!skb_pull(skb, sizeof(*src)))
			return -EPROTO;
			return -EPROTO;


		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
		dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
		if (!dst)
		if (!dst)
			continue;
			continue;


		ath10k_wmi_10_4_pull_peer_stats(&src->common, dst);
		ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
		/* FIXME: expose 10.4 specific values */
		if (extd_peer_stats)
		dst->rx_duration = __le32_to_cpu(src->rx_duration);
		dst->rx_duration = __le32_to_cpu(src->rx_duration);

		list_add_tail(&dst->list, &stats->peers_extd);
		list_add_tail(&dst->list, &stats->peers);
	}
	}


	return 0;
	return 0;
Loading