ath10k: fix fw stats processing
If stat data exceeds wmi-htc buffer limits
firmware splits it into many wmi stats update
events which are delivered in a ping-pong fashion
triggered by wmi stats request command.
Since there's only an implicit start-of-data and
no end-of-data indications the driver has to
perform some trickery to get complete stat data.
kvalo: use %zu to fix a compiler warning and fix a typo in a comment
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index c4640aa..f65032f 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1330,7 +1330,7 @@
}
static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
- struct ath10k_fw_stats *dst)
+ struct ath10k_fw_stats_pdev *dst)
{
const struct wal_dbg_tx_stats *tx = &src->wal.tx;
const struct wal_dbg_rx_stats *rx = &src->wal.rx;
@@ -1405,26 +1405,38 @@
num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
- if (num_pdev_stats) {
+ for (i = 0; i < num_pdev_stats; i++) {
const struct wmi_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
- ath10k_wmi_pull_pdev_stats(src, stats);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_pdev_stats(src, dst);
+ list_add_tail(&dst->list, &stats->pdevs);
}
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
- ath10k_wmi_pull_peer_stats(src, &stats->peer_stat[i]);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_peer_stats(src, dst);
+ list_add_tail(&dst->list, &stats->peers);
}
return 0;
@@ -1445,36 +1457,49 @@
num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
- if (num_pdev_stats) {
+ for (i = 0; i < num_pdev_stats; i++) {
const struct wmi_10x_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
- ath10k_wmi_pull_pdev_stats(&src->old, stats);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
- stats->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
- stats->rts_bad = __le32_to_cpu(src->rts_bad);
- stats->rts_good = __le32_to_cpu(src->rts_good);
- stats->fcs_bad = __le32_to_cpu(src->fcs_bad);
- stats->no_beacons = __le32_to_cpu(src->no_beacons);
- stats->mib_int_count = __le32_to_cpu(src->mib_int_count);
+ ath10k_wmi_pull_pdev_stats(&src->old, dst);
+
+ dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
+ dst->rts_bad = __le32_to_cpu(src->rts_bad);
+ dst->rts_good = __le32_to_cpu(src->rts_good);
+ dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
+ dst->no_beacons = __le32_to_cpu(src->no_beacons);
+ dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
+
+ list_add_tail(&dst->list, &stats->pdevs);
}
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_10x_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
return -EPROTO;
- ath10k_wmi_pull_peer_stats(&src->old, &stats->peer_stat[i]);
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
- stats->peer_stat[i].peer_rx_rate =
- __le32_to_cpu(src->peer_rx_rate);
+ ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+
+ list_add_tail(&dst->list, &stats->peers);
}
return 0;