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>
This commit is contained in:
Michal Kazior
2014-09-25 12:33:50 +02:00
committed by Kalle Valo
parent fb2e9c0cc7
commit 5326849a86
3 changed files with 252 additions and 95 deletions

View File

@@ -1330,7 +1330,7 @@ static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
}
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 @@ static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
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 @@ static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
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;