Hi,
This cleans up and fixes fw stats aggregation.
Michal Kazior (4):
ath10k: split wmi stats parsing
ath10k: rename fw_stats related stuff
ath10k: request fw_stats once on open
ath10k: fix fw stats processing
drivers/net/wireless/ath/ath10k/core.h | 26 +-
drivers/net/wireless/ath/ath10k/debug.c | 476 ++++++++++++++++++--------------
drivers/net/wireless/ath/ath10k/debug.h | 7 +-
drivers/net/wireless/ath/ath10k/wmi.c | 190 ++++++++++++-
drivers/net/wireless/ath/ath10k/wmi.h | 24 +-
5 files changed, 483 insertions(+), 240 deletions(-)
--
1.8.5.3
Michal Kazior <[email protected]> writes:
> On 27 September 2014 10:01, Kalle Valo <[email protected]> wrote:
>> Michal Kazior <[email protected]> writes:
>>
>>> 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.
>>>
>>> Signed-off-by: Michal Kazior <[email protected]>
>>
>> I folded the patch below in ath-next-next. Please check:
>>
>> https://github.com/kvalo/ath/commit/7071112c5ddaff761cd7d9bb3110c3f3d2f83bb8
> [...]
>
> I think you meant:
>
> https://github.com/kvalo/ath/commit/5122a60f4fbf5a38e70a14578858b39058ee367a
Indeed. Sorry for the mixup.
> It looks good. Thanks!
Thanks for checking.
--
Kalle Valo
On 26 September 2014 13:06, Kalle Valo <[email protected]> wrote:
> Michal Kazior <[email protected]> writes:
>> Hi,
>>
>> This cleans up and fixes fw stats aggregation.
>>
>>
>> Michal Kazior (4):
>> ath10k: split wmi stats parsing
>> ath10k: rename fw_stats related stuff
>> ath10k: request fw_stats once on open
>> ath10k: fix fw stats processing
>
> I didn't review these yet but I saw new warnings:
>
> drivers/net/wireless/ath/ath10k/debug.c: In function 'ath10k_fw_stats_fill':
> drivers/net/wireless/ath/ath10k/debug.c:540:5: warning: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'size_t' [-Wformat]
Oh, I didn't see this one. I guess it should be %zu.
> drivers/net/wireless/ath/ath10k/wmi.c:1513: WARNING: else is not generally useful after a break or return
I'm aware of this one but the code looks nicer with the `else` to me though.
Michał
Michal Kazior <[email protected]> writes:
> Hi,
>
> This cleans up and fixes fw stats aggregation.
>
>
> Michal Kazior (4):
> ath10k: split wmi stats parsing
> ath10k: rename fw_stats related stuff
> ath10k: request fw_stats once on open
> ath10k: fix fw stats processing
I didn't review these yet but I saw new warnings:
drivers/net/wireless/ath/ath10k/debug.c: In function 'ath10k_fw_stats_fill':
drivers/net/wireless/ath/ath10k/debug.c:540:5: warning: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'size_t' [-Wformat]
drivers/net/wireless/ath/ath10k/wmi.c:1513: WARNING: else is not generally useful after a break or return
--
Kalle Valo
On 27 September 2014 10:01, Kalle Valo <[email protected]> wrote:
> Michal Kazior <[email protected]> writes:
>
>> 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.
>>
>> Signed-off-by: Michal Kazior <[email protected]>
>
> I folded the patch below in ath-next-next. Please check:
>
> https://github.com/kvalo/ath/commit/7071112c5ddaff761cd7d9bb3110c3f3d2f83bb8
[...]
I think you meant:
https://github.com/kvalo/ath/commit/5122a60f4fbf5a38e70a14578858b39058ee367a
It looks good. Thanks!
Michał
The naming was a bit inconsistent.
Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 11 +++++------
drivers/net/wireless/ath/ath10k/debug.c | 18 +++++++++---------
drivers/net/wireless/ath/ath10k/debug.h | 6 +++---
drivers/net/wireless/ath/ath10k/wmi.c | 12 ++++++------
drivers/net/wireless/ath/ath10k/wmi.h | 4 ++--
5 files changed, 25 insertions(+), 26 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8a997b6..082a8dad 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -116,14 +116,14 @@ struct ath10k_wmi {
struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS];
};
-struct ath10k_peer_stat {
+struct ath10k_fw_stats_peer {
u8 peer_macaddr[ETH_ALEN];
u32 peer_rssi;
u32 peer_tx_rate;
u32 peer_rx_rate; /* 10x only */
};
-struct ath10k_target_stats {
+struct ath10k_fw_stats {
/* PDEV stats */
s32 ch_noise_floor;
u32 tx_frame_count;
@@ -183,7 +183,7 @@ struct ath10k_target_stats {
/* PEER STATS */
u8 peers;
- struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+ struct ath10k_fw_stats_peer peer_stat[TARGET_NUM_PEERS];
/* TODO: Beacon filter stats */
@@ -292,11 +292,10 @@ struct ath10k_fw_crash_data {
struct ath10k_debug {
struct dentry *debugfs_phy;
- struct ath10k_target_stats target_stats;
+ struct ath10k_fw_stats fw_stats;
+ struct completion fw_stats_complete;
DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
- struct completion event_stats_compl;
-
unsigned long htt_stats_mask;
struct delayed_work htt_stats_dwork;
struct ath10k_dfs_stats dfs_stats;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 0afd1aa..97b5c16 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -242,29 +242,29 @@ static const struct file_operations fops_wmi_services = {
.llseek = default_llseek,
};
-void ath10k_debug_read_target_stats(struct ath10k *ar, struct sk_buff *skb)
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
{
int ret;
spin_lock_bh(&ar->data_lock);
- ret = ath10k_wmi_pull_fw_stats(ar, skb, &ar->debug.target_stats);
+ ret = ath10k_wmi_pull_fw_stats(ar, skb, &ar->debug.fw_stats);
if (ret) {
ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
goto unlock;
}
- complete(&ar->debug.event_stats_compl);
+ complete(&ar->debug.fw_stats_complete);
unlock:
spin_unlock_bh(&ar->data_lock);
}
-static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
- struct ath10k_target_stats *fw_stats;
+ struct ath10k_fw_stats *fw_stats;
char *buf = NULL;
unsigned int len = 0, buf_len = 8000;
ssize_t ret_cnt = 0;
@@ -272,7 +272,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
int i;
int ret;
- fw_stats = &ar->debug.target_stats;
+ fw_stats = &ar->debug.fw_stats;
mutex_lock(&ar->conf_mutex);
@@ -289,7 +289,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
goto exit;
}
- left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+ left = wait_for_completion_timeout(&ar->debug.fw_stats_complete, 1*HZ);
if (left <= 0)
goto exit;
@@ -448,7 +448,7 @@ exit:
}
static const struct file_operations fops_fw_stats = {
- .read = ath10k_read_fw_stats,
+ .read = ath10k_fw_stats_read,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
@@ -1072,7 +1072,7 @@ int ath10k_debug_register(struct ath10k *ar)
INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
ath10k_debug_htt_stats_dwork);
- init_completion(&ar->debug.event_stats_compl);
+ init_completion(&ar->debug.fw_stats_complete);
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_fw_stats);
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index f7e8131..8d15bce 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -55,7 +55,7 @@ void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_read_service_map(struct ath10k *ar,
const void *service_map,
size_t map_size);
-void ath10k_debug_read_target_stats(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
@@ -97,8 +97,8 @@ static inline void ath10k_debug_read_service_map(struct ath10k *ar,
{
}
-static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct sk_buff *skb)
+static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
+ struct sk_buff *skb)
{
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 8e2d021..c4640aa 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -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_target_stats *dst)
+ struct ath10k_fw_stats *dst)
{
const struct wal_dbg_tx_stats *tx = &src->wal.tx;
const struct wal_dbg_rx_stats *rx = &src->wal.rx;
@@ -1383,7 +1383,7 @@ static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
}
static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
- struct ath10k_peer_stat *dst)
+ struct ath10k_fw_stats_peer *dst)
{
ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
@@ -1392,7 +1392,7 @@ static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
- struct ath10k_target_stats *stats)
+ struct ath10k_fw_stats *stats)
{
const struct wmi_stats_event *ev = (void *)skb->data;
u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
@@ -1432,7 +1432,7 @@ static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
- struct ath10k_target_stats *stats)
+ struct ath10k_fw_stats *stats)
{
const struct wmi_stats_event *ev = (void *)skb->data;
u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
@@ -1481,7 +1481,7 @@ static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
}
int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
- struct ath10k_target_stats *stats)
+ struct ath10k_fw_stats *stats)
{
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
@@ -1493,7 +1493,7 @@ static void ath10k_wmi_event_update_stats(struct ath10k *ar,
struct sk_buff *skb)
{
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
- ath10k_debug_read_target_stats(ar, skb);
+ ath10k_debug_fw_stats_process(ar, skb);
}
static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 4ce8e3c..6243dbe 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -4573,7 +4573,7 @@ struct wmi_svc_rdy_ev_arg {
struct ath10k;
struct ath10k_vif;
-struct ath10k_target_stats;
+struct ath10k_fw_stats;
int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar);
@@ -4646,6 +4646,6 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
- struct ath10k_target_stats *stats);
+ struct ath10k_fw_stats *stats);
#endif /* _WMI_H_ */
--
1.8.5.3
The parsing function was rather complex. Simplify
by splitting it up into firmware branch specific
implementations.
While at it move the parsing code into wmi.c where
it belongs.
Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/debug.c | 127 ++----------------------
drivers/net/wireless/ath/ath10k/debug.h | 5 +-
drivers/net/wireless/ath/ath10k/wmi.c | 165 +++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath10k/wmi.h | 24 ++---
4 files changed, 180 insertions(+), 141 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 101c6f9..0afd1aa 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -242,133 +242,22 @@ static const struct file_operations fops_wmi_services = {
.llseek = default_llseek,
};
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+void ath10k_debug_read_target_stats(struct ath10k *ar, struct sk_buff *skb)
{
- u8 *tmp = ev->data;
- struct ath10k_target_stats *stats;
- int num_pdev_stats, num_vdev_stats, num_peer_stats;
- struct wmi_pdev_stats_10x *ps;
- int i;
+ int ret;
spin_lock_bh(&ar->data_lock);
- stats = &ar->debug.target_stats;
-
- num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
- num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
- num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
-
- if (num_pdev_stats) {
- ps = (struct wmi_pdev_stats_10x *)tmp;
-
- stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
- stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
- stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
- stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
- stats->cycle_count = __le32_to_cpu(ps->cycle_count);
- stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
- stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
-
- stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
- stats->comp_delivered =
- __le32_to_cpu(ps->wal.tx.comp_delivered);
- stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
- stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
- stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
- stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
- stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
- stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
- stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
- stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
- stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
- stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
- stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
- stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
- stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
- stats->sw_retry_failure =
- __le32_to_cpu(ps->wal.tx.sw_retry_failure);
- stats->illgl_rate_phy_err =
- __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
- stats->pdev_cont_xretry =
- __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
- stats->pdev_tx_timeout =
- __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
- stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
- stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
- stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
-
- stats->mid_ppdu_route_change =
- __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
- stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
- stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
- stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
- stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
- stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
- stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
- stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
- stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
- stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
- stats->oversize_amsdu =
- __le32_to_cpu(ps->wal.rx.oversize_amsdu);
- stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
- stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
- stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
-
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
- stats->rts_bad = __le32_to_cpu(ps->rts_bad);
- stats->rts_good = __le32_to_cpu(ps->rts_good);
- stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
- stats->no_beacons = __le32_to_cpu(ps->no_beacons);
- stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
- tmp += sizeof(struct wmi_pdev_stats_10x);
- } else {
- tmp += sizeof(struct wmi_pdev_stats_old);
- }
- }
-
- /* 0 or max vdevs */
- /* Currently firmware does not support VDEV stats */
- if (num_vdev_stats) {
- struct wmi_vdev_stats *vdev_stats;
-
- for (i = 0; i < num_vdev_stats; i++) {
- vdev_stats = (struct wmi_vdev_stats *)tmp;
- tmp += sizeof(struct wmi_vdev_stats);
- }
+ ret = ath10k_wmi_pull_fw_stats(ar, skb, &ar->debug.target_stats);
+ if (ret) {
+ ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
+ goto unlock;
}
- if (num_peer_stats) {
- struct wmi_peer_stats_10x *peer_stats;
- struct ath10k_peer_stat *s;
-
- stats->peers = num_peer_stats;
-
- for (i = 0; i < num_peer_stats; i++) {
- peer_stats = (struct wmi_peer_stats_10x *)tmp;
- s = &stats->peer_stat[i];
-
- memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
- ETH_ALEN);
- s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
- s->peer_tx_rate =
- __le32_to_cpu(peer_stats->peer_tx_rate);
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- s->peer_rx_rate =
- __le32_to_cpu(peer_stats->peer_rx_rate);
- tmp += sizeof(struct wmi_peer_stats_10x);
-
- } else {
- tmp += sizeof(struct wmi_peer_stats_old);
- }
- }
- }
+ complete(&ar->debug.event_stats_compl);
+unlock:
spin_unlock_bh(&ar->data_lock);
- complete(&ar->debug.event_stats_compl);
}
static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index d6276fe..f7e8131 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -55,8 +55,7 @@ void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_read_service_map(struct ath10k *ar,
const void *service_map,
size_t map_size);
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev);
+void ath10k_debug_read_target_stats(struct ath10k *ar, struct sk_buff *skb);
struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
@@ -99,7 +98,7 @@ static inline void ath10k_debug_read_service_map(struct ath10k *ar,
}
static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+ struct sk_buff *skb)
{
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index bfc1fb3..8e2d021 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1329,14 +1329,171 @@ static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
+static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
+ struct ath10k_target_stats *dst)
+{
+ const struct wal_dbg_tx_stats *tx = &src->wal.tx;
+ const struct wal_dbg_rx_stats *rx = &src->wal.rx;
+
+ dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
+ dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
+ dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
+ dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count);
+ dst->cycle_count = __le32_to_cpu(src->cycle_count);
+ dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
+ dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
+
+ dst->comp_queued = __le32_to_cpu(tx->comp_queued);
+ dst->comp_delivered = __le32_to_cpu(tx->comp_delivered);
+ dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued);
+ dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued);
+ dst->wmm_drop = __le32_to_cpu(tx->wmm_drop);
+ dst->local_enqued = __le32_to_cpu(tx->local_enqued);
+ dst->local_freed = __le32_to_cpu(tx->local_freed);
+ dst->hw_queued = __le32_to_cpu(tx->hw_queued);
+ dst->hw_reaped = __le32_to_cpu(tx->hw_reaped);
+ dst->underrun = __le32_to_cpu(tx->underrun);
+ dst->tx_abort = __le32_to_cpu(tx->tx_abort);
+ dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed);
+ dst->tx_ko = __le32_to_cpu(tx->tx_ko);
+ dst->data_rc = __le32_to_cpu(tx->data_rc);
+ dst->self_triggers = __le32_to_cpu(tx->self_triggers);
+ dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure);
+ dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err);
+ dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry);
+ dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout);
+ dst->pdev_resets = __le32_to_cpu(tx->pdev_resets);
+ dst->phy_underrun = __le32_to_cpu(tx->phy_underrun);
+ dst->txop_ovf = __le32_to_cpu(tx->txop_ovf);
+
+ dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change);
+ dst->status_rcvd = __le32_to_cpu(rx->status_rcvd);
+ dst->r0_frags = __le32_to_cpu(rx->r0_frags);
+ dst->r1_frags = __le32_to_cpu(rx->r1_frags);
+ dst->r2_frags = __le32_to_cpu(rx->r2_frags);
+ dst->r3_frags = __le32_to_cpu(rx->r3_frags);
+ dst->htt_msdus = __le32_to_cpu(rx->htt_msdus);
+ dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus);
+ dst->loc_msdus = __le32_to_cpu(rx->loc_msdus);
+ dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus);
+ dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu);
+ dst->phy_errs = __le32_to_cpu(rx->phy_errs);
+ dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop);
+ dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
+}
+
+static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
+ struct ath10k_peer_stat *dst)
+{
+ ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
+ dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
+ dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
+}
+
+static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_target_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ if (num_pdev_stats) {
+ const struct wmi_pdev_stats *src;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ ath10k_wmi_pull_pdev_stats(src, stats);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_peer_stats *src;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ ath10k_wmi_pull_peer_stats(src, &stats->peer_stat[i]);
+ }
+
+ return 0;
+}
+
+static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_target_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ if (num_pdev_stats) {
+ const struct wmi_10x_pdev_stats *src;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ ath10k_wmi_pull_pdev_stats(&src->old, stats);
+
+ 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);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_10x_peer_stats *src;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ ath10k_wmi_pull_peer_stats(&src->old, &stats->peer_stat[i]);
+
+ stats->peer_stat[i].peer_rx_rate =
+ __le32_to_cpu(src->peer_rx_rate);
+ }
+
+ return 0;
+}
+
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_target_stats *stats)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+ return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
+ else
+ return ath10k_wmi_main_pull_fw_stats(ar, skb, stats);
+}
+
static void ath10k_wmi_event_update_stats(struct ath10k *ar,
struct sk_buff *skb)
{
- struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
-
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
-
- ath10k_debug_read_target_stats(ar, ev);
+ ath10k_debug_read_target_stats(ar, skb);
}
static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 55d8317..4ce8e3c 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -3021,7 +3021,7 @@ struct wmi_stats_event {
* PDEV statistics
* TODO: add all PDEV stats here
*/
-struct wmi_pdev_stats_old {
+struct wmi_pdev_stats {
__le32 chan_nf; /* Channel noise floor */
__le32 tx_frame_count; /* TX frame count */
__le32 rx_frame_count; /* RX frame count */
@@ -3032,15 +3032,8 @@ struct wmi_pdev_stats_old {
struct wal_dbg_stats wal; /* WAL dbg stats */
} __packed;
-struct wmi_pdev_stats_10x {
- __le32 chan_nf; /* Channel noise floor */
- __le32 tx_frame_count; /* TX frame count */
- __le32 rx_frame_count; /* RX frame count */
- __le32 rx_clear_count; /* rx clear count */
- __le32 cycle_count; /* cycle count */
- __le32 phy_err_count; /* Phy error count */
- __le32 chan_tx_pwr; /* channel tx power */
- struct wal_dbg_stats wal; /* WAL dbg stats */
+struct wmi_10x_pdev_stats {
+ struct wmi_pdev_stats old;
__le32 ack_rx_bad;
__le32 rts_bad;
__le32 rts_good;
@@ -3061,16 +3054,14 @@ struct wmi_vdev_stats {
* peer statistics.
* TODO: add more stats
*/
-struct wmi_peer_stats_old {
+struct wmi_peer_stats {
struct wmi_mac_addr peer_macaddr;
__le32 peer_rssi;
__le32 peer_tx_rate;
} __packed;
-struct wmi_peer_stats_10x {
- struct wmi_mac_addr peer_macaddr;
- __le32 peer_rssi;
- __le32 peer_tx_rate;
+struct wmi_10x_peer_stats {
+ struct wmi_peer_stats old;
__le32 peer_rx_rate;
} __packed;
@@ -4582,6 +4573,7 @@ struct wmi_svc_rdy_ev_arg {
struct ath10k;
struct ath10k_vif;
+struct ath10k_target_stats;
int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar);
@@ -4653,5 +4645,7 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
enum wmi_force_fw_hang_type type, u32 delay_ms);
int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_target_stats *stats);
#endif /* _WMI_H_ */
--
1.8.5.3
Michal Kazior <[email protected]> writes:
> 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.
>
> Signed-off-by: Michal Kazior <[email protected]>
I folded the patch below in ath-next-next. Please check:
https://github.com/kvalo/ath/commit/7071112c5ddaff761cd7d9bb3110c3f3d2f83bb8
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 0e6626282664..680d5088b6f3 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -343,7 +343,7 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
free:
/* In some cases lists have been spliced and cleared. Free up
- * resouruces if that is not the case.
+ * resources if that is not the case.
*/
ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
ath10k_debug_fw_stats_peers_free(&stats.peers);
@@ -536,7 +536,7 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
"MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
len += scnprintf(buf + len, buf_len - len, "\n");
- len += scnprintf(buf + len, buf_len - len, "%30s (%lu)\n",
+ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
"ath10k PEER stats", num_peers);
len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
"=================");
--
Kalle Valo
Michal Kazior <[email protected]> writes:
> This cleans up and fixes fw stats aggregation.
>
>
> Michal Kazior (4):
> ath10k: split wmi stats parsing
> ath10k: rename fw_stats related stuff
> ath10k: request fw_stats once on open
> ath10k: fix fw stats processing
Thanks, applied.
And thank you for fixing this for good, it has been buggy for way too
long.
--
Kalle Valo
Stats were requested and processed for each read
call. This caused inconsistent readings.
Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/debug.c | 110 +++++++++++++++++++++++---------
1 file changed, 79 insertions(+), 31 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 97b5c16..1ab0f10 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -260,38 +260,35 @@ unlock:
spin_unlock_bh(&ar->data_lock);
}
-static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int ath10k_fw_stats_request(struct ath10k *ar)
{
- struct ath10k *ar = file->private_data;
- struct ath10k_fw_stats *fw_stats;
- char *buf = NULL;
- unsigned int len = 0, buf_len = 8000;
- ssize_t ret_cnt = 0;
- long left;
- int i;
int ret;
- fw_stats = &ar->debug.fw_stats;
-
- mutex_lock(&ar->conf_mutex);
-
- if (ar->state != ATH10K_STATE_ON)
- goto exit;
-
- buf = kzalloc(buf_len, GFP_KERNEL);
- if (!buf)
- goto exit;
+ lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
if (ret) {
- ath10k_warn(ar, "could not request stats (%d)\n", ret);
- goto exit;
+ ath10k_warn(ar, "failed to fw stat request command: %d\n", ret);
+ return ret;
}
- left = wait_for_completion_timeout(&ar->debug.fw_stats_complete, 1*HZ);
- if (left <= 0)
- goto exit;
+ ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete, 1*HZ);
+ if (ret <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/* FIXME: How to calculate the buffer size sanely? */
+#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
+
+static void ath10k_fw_stats_fill(struct ath10k *ar,
+ struct ath10k_fw_stats *fw_stats,
+ char *buf)
+{
+ unsigned int len = 0;
+ unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
+ int i;
spin_lock_bh(&ar->data_lock);
len += scnprintf(buf + len, buf_len - len, "\n");
@@ -436,20 +433,71 @@ static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
}
spin_unlock_bh(&ar->data_lock);
- if (len > buf_len)
- len = buf_len;
+ if (len >= buf_len)
+ buf[len - 1] = 0;
+ else
+ buf[len] = 0;
+}
- ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf = NULL;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
+ ret = ath10k_fw_stats_request(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
+ goto err_free;
+ }
+
+ ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
+ file->private_data = buf;
-exit:
mutex_unlock(&ar->conf_mutex);
- kfree(buf);
- return ret_cnt;
+ return 0;
+
+err_free:
+ vfree(buf);
+
+err_unlock:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ unsigned int len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_fw_stats = {
+ .open = ath10k_fw_stats_open,
+ .release = ath10k_fw_stats_release,
.read = ath10k_fw_stats_read,
- .open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
--
1.8.5.3
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.
Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 19 +--
drivers/net/wireless/ath/ath10k/debug.c | 273 +++++++++++++++++++++++---------
drivers/net/wireless/ath/ath10k/wmi.c | 55 +++++--
3 files changed, 252 insertions(+), 95 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 082a8dad..674e38c 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -117,13 +117,17 @@ struct ath10k_wmi {
};
struct ath10k_fw_stats_peer {
+ struct list_head list;
+
u8 peer_macaddr[ETH_ALEN];
u32 peer_rssi;
u32 peer_tx_rate;
u32 peer_rx_rate; /* 10x only */
};
-struct ath10k_fw_stats {
+struct ath10k_fw_stats_pdev {
+ struct list_head list;
+
/* PDEV stats */
s32 ch_noise_floor;
u32 tx_frame_count;
@@ -178,15 +182,11 @@ struct ath10k_fw_stats {
s32 phy_errs;
s32 phy_err_drop;
s32 mpdu_errs;
+};
- /* VDEV STATS */
-
- /* PEER STATS */
- u8 peers;
- struct ath10k_fw_stats_peer peer_stat[TARGET_NUM_PEERS];
-
- /* TODO: Beacon filter stats */
-
+struct ath10k_fw_stats {
+ struct list_head pdevs;
+ struct list_head peers;
};
struct ath10k_dfs_stats {
@@ -294,6 +294,7 @@ struct ath10k_debug {
struct ath10k_fw_stats fw_stats;
struct completion fw_stats_complete;
+ bool fw_stats_done;
DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
unsigned long htt_stats_mask;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 1ab0f10..4724172 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -242,39 +242,154 @@ static const struct file_operations fops_wmi_services = {
.llseek = default_llseek,
};
+static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
+{
+ struct ath10k_fw_stats_pdev *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
+{
+ struct ath10k_fw_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)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->debug.fw_stats_done = false;
+ ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
+ spin_unlock_bh(&ar->data_lock);
+}
+
+static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
+{
+ struct ath10k_fw_stats_peer *i;
+ size_t num = 0;
+
+ list_for_each_entry(i, head, list)
+ ++num;
+
+ return num;
+}
+
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
{
+ struct ath10k_fw_stats stats = {};
+ bool is_start, is_started, is_end;
+ size_t num_peers;
int ret;
- spin_lock_bh(&ar->data_lock);
+ INIT_LIST_HEAD(&stats.pdevs);
+ INIT_LIST_HEAD(&stats.peers);
- ret = ath10k_wmi_pull_fw_stats(ar, skb, &ar->debug.fw_stats);
+ spin_lock_bh(&ar->data_lock);
+ ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
if (ret) {
ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
goto unlock;
}
+ /* Stat data may exceed htc-wmi buffer limit. In such case firmware
+ * splits the stats data and delivers it in a ping-pong fashion of
+ * request cmd-update event.
+ *
+ * However there is no explicit end-of-data. Instead start-of-data is
+ * used as an implicit one. This works as follows:
+ * a) discard stat update events until one with pdev stats is
+ * delivered - this skips session started at end of (b)
+ * b) consume stat update events until another one with pdev stats is
+ * delivered which is treated as end-of-data and is itself discarded
+ */
+
+ if (ar->debug.fw_stats_done) {
+ ath10k_warn(ar, "received unsolicited stats update event\n");
+ goto free;
+ }
+
+ num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+ is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+ is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+
+ if (is_start)
+ list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
+
+ if (is_end)
+ ar->debug.fw_stats_done = true;
+
+ is_started = !list_empty(&ar->debug.fw_stats.pdevs);
+
+ if (is_started && !is_end) {
+ if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
+ /* Although this is unlikely impose a sane limit to
+ * prevent firmware from DoS-ing the host.
+ */
+ ath10k_warn(ar, "dropping fw peer stats\n");
+ goto free;
+ }
+
+ list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
+ }
+
complete(&ar->debug.fw_stats_complete);
+free:
+ /* In some cases lists have been spliced and cleared. Free up
+ * resouruces if that is not the case.
+ */
+ ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&stats.peers);
+
unlock:
spin_unlock_bh(&ar->data_lock);
}
-static int ath10k_fw_stats_request(struct ath10k *ar)
+static int ath10k_debug_fw_stats_request(struct ath10k *ar)
{
+ unsigned long timeout;
int ret;
lockdep_assert_held(&ar->conf_mutex);
- ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
- if (ret) {
- ath10k_warn(ar, "failed to fw stat request command: %d\n", ret);
- return ret;
- }
+ timeout = jiffies + msecs_to_jiffies(1*HZ);
+
+ ath10k_debug_fw_stats_reset(ar);
+
+ for (;;) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ reinit_completion(&ar->debug.fw_stats_complete);
+
+ ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+ if (ret) {
+ ath10k_warn(ar, "could not request stats (%d)\n", ret);
+ return ret;
+ }
- ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete, 1*HZ);
- if (ret <= 0)
- return -ETIMEDOUT;
+ ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
+ 1*HZ);
+ if (ret <= 0)
+ return -ETIMEDOUT;
+
+ spin_lock_bh(&ar->data_lock);
+ if (ar->debug.fw_stats_done) {
+ spin_unlock_bh(&ar->data_lock);
+ break;
+ }
+ spin_unlock_bh(&ar->data_lock);
+ }
return 0;
}
@@ -288,9 +403,21 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
{
unsigned int len = 0;
unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
- int i;
+ const struct ath10k_fw_stats_pdev *pdev;
+ const struct ath10k_fw_stats_peer *peer;
+ size_t num_peers;
spin_lock_bh(&ar->data_lock);
+
+ pdev = list_first_entry_or_null(&fw_stats->pdevs,
+ struct ath10k_fw_stats_pdev, list);
+ if (!pdev) {
+ ath10k_warn(ar, "failed to get pdev stats\n");
+ goto unlock;
+ }
+
+ num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
+
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
"ath10k PDEV stats");
@@ -298,29 +425,29 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Channel noise floor", fw_stats->ch_noise_floor);
+ "Channel noise floor", pdev->ch_noise_floor);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Channel TX power", fw_stats->chan_tx_power);
+ "Channel TX power", pdev->chan_tx_power);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "TX frame count", fw_stats->tx_frame_count);
+ "TX frame count", pdev->tx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX frame count", fw_stats->rx_frame_count);
+ "RX frame count", pdev->rx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX clear count", fw_stats->rx_clear_count);
+ "RX clear count", pdev->rx_clear_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Cycle count", fw_stats->cycle_count);
+ "Cycle count", pdev->cycle_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "PHY error count", fw_stats->phy_err_count);
+ "PHY error count", pdev->phy_err_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS bad count", fw_stats->rts_bad);
+ "RTS bad count", pdev->rts_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS good count", fw_stats->rts_good);
+ "RTS good count", pdev->rts_good);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "FCS bad count", fw_stats->fcs_bad);
+ "FCS bad count", pdev->fcs_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "No beacon count", fw_stats->no_beacons);
+ "No beacon count", pdev->no_beacons);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "MIB int count", fw_stats->mib_int_count);
+ "MIB int count", pdev->mib_int_count);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -329,51 +456,51 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies queued", fw_stats->comp_queued);
+ "HTT cookies queued", pdev->comp_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies disp.", fw_stats->comp_delivered);
+ "HTT cookies disp.", pdev->comp_delivered);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDU queued", fw_stats->msdu_enqued);
+ "MSDU queued", pdev->msdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU queued", fw_stats->mpdu_enqued);
+ "MPDU queued", pdev->mpdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs dropped", fw_stats->wmm_drop);
+ "MSDUs dropped", pdev->wmm_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local enqued", fw_stats->local_enqued);
+ "Local enqued", pdev->local_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local freed", fw_stats->local_freed);
+ "Local freed", pdev->local_freed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW queued", fw_stats->hw_queued);
+ "HW queued", pdev->hw_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs reaped", fw_stats->hw_reaped);
+ "PPDUs reaped", pdev->hw_reaped);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Num underruns", fw_stats->underrun);
+ "Num underruns", pdev->underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs cleaned", fw_stats->tx_abort);
+ "PPDUs cleaned", pdev->tx_abort);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs requed", fw_stats->mpdus_requed);
+ "MPDUs requed", pdev->mpdus_requed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Excessive retries", fw_stats->tx_ko);
+ "Excessive retries", pdev->tx_ko);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW rate", fw_stats->data_rc);
+ "HW rate", pdev->data_rc);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Sched self tiggers", fw_stats->self_triggers);
+ "Sched self tiggers", pdev->self_triggers);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Dropped due to SW retries",
- fw_stats->sw_retry_failure);
+ pdev->sw_retry_failure);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Illegal rate phy errors",
- fw_stats->illgl_rate_phy_err);
+ pdev->illgl_rate_phy_err);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+ "Pdev continous xretry", pdev->pdev_cont_xretry);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "TX timeout", fw_stats->pdev_tx_timeout);
+ "TX timeout", pdev->pdev_tx_timeout);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PDEV resets", fw_stats->pdev_resets);
+ "PDEV resets", pdev->pdev_resets);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY underrun", fw_stats->phy_underrun);
+ "PHY underrun", pdev->phy_underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU is more than txop limit", fw_stats->txop_ovf);
+ "MPDU is more than txop limit", pdev->txop_ovf);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -383,54 +510,53 @@ static void ath10k_fw_stats_fill(struct ath10k *ar,
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Mid PPDU route change",
- fw_stats->mid_ppdu_route_change);
+ pdev->mid_ppdu_route_change);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Tot. number of statuses", fw_stats->status_rcvd);
+ "Tot. number of statuses", pdev->status_rcvd);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 0", fw_stats->r0_frags);
+ "Extra frags on rings 0", pdev->r0_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 1", fw_stats->r1_frags);
+ "Extra frags on rings 1", pdev->r1_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 2", fw_stats->r2_frags);
+ "Extra frags on rings 2", pdev->r2_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 3", fw_stats->r3_frags);
+ "Extra frags on rings 3", pdev->r3_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to HTT", fw_stats->htt_msdus);
+ "MSDUs delivered to HTT", pdev->htt_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+ "MPDUs delivered to HTT", pdev->htt_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to stack", fw_stats->loc_msdus);
+ "MSDUs delivered to stack", pdev->loc_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to stack", fw_stats->loc_mpdus);
+ "MPDUs delivered to stack", pdev->loc_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Oversized AMSUs", fw_stats->oversize_amsdu);
+ "Oversized AMSUs", pdev->oversize_amsdu);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors", fw_stats->phy_errs);
+ "PHY errors", pdev->phy_errs);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors drops", fw_stats->phy_err_drop);
+ "PHY errors drops", pdev->phy_err_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+ "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
len += scnprintf(buf + len, buf_len - len, "\n");
- len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n",
- "ath10k PEER stats", fw_stats->peers);
+ len += scnprintf(buf + len, buf_len - len, "%30s (%lu)\n",
+ "ath10k PEER stats", num_peers);
len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
"=================");
- for (i = 0; i < fw_stats->peers; i++) {
+ list_for_each_entry(peer, &fw_stats->peers, list) {
len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
- "Peer MAC address",
- fw_stats->peer_stat[i].peer_macaddr);
+ "Peer MAC address", peer->peer_macaddr);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+ "Peer RSSI", peer->peer_rssi);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer TX rate",
- fw_stats->peer_stat[i].peer_tx_rate);
+ "Peer TX rate", peer->peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RX rate",
- fw_stats->peer_stat[i].peer_rx_rate);
+ "Peer RX rate", peer->peer_rx_rate);
len += scnprintf(buf + len, buf_len - len, "\n");
}
+
+unlock:
spin_unlock_bh(&ar->data_lock);
if (len >= buf_len)
@@ -458,7 +584,7 @@ static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
goto err_unlock;
}
- ret = ath10k_fw_stats_request(ar);
+ ret = ath10k_debug_fw_stats_request(ar);
if (ret) {
ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
goto err_free;
@@ -1097,6 +1223,9 @@ int ath10k_debug_create(struct ath10k *ar)
if (!ar->debug.fw_crash_data)
return -ENOMEM;
+ INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
+ INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
+
return 0;
}
@@ -1104,6 +1233,8 @@ void ath10k_debug_destroy(struct ath10k *ar)
{
vfree(ar->debug.fw_crash_data);
ar->debug.fw_crash_data = NULL;
+
+ ath10k_debug_fw_stats_reset(ar);
}
int ath10k_debug_register(struct ath10k *ar)
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 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;
+
+ 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);
- 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);
+ 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;
+
+ ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
- stats->peer_stat[i].peer_rx_rate =
- __le32_to_cpu(src->peer_rx_rate);
+ list_add_tail(&dst->list, &stats->peers);
}
return 0;
--
1.8.5.3
On Fri, 2014-09-26 at 13:14 +0200, Michal Kazior wrote:
> On 26 September 2014 13:06, Kalle Valo <[email protected]> wrote:
> > drivers/net/wireless/ath/ath10k/wmi.c:1513: WARNING: else is not generally useful after a break or return
> I'm aware of this one but the code looks nicer with the `else` to me though.
Yay Michal.
Always use your taste over any mindless style tool.
Ignore any checkpatch complaint you want.
btw: checkpatch shouldn't warn about this as of:
commit adefeef310ee8bd3923d41b67bc53c398bc53621
Author: Joe Perches <[email protected]>
Date: Fri Sep 26 10:19:29 2014 +1000
checkpatch: add exception to return then else test
Add an exception to the return before else warning
when the line following it is also a return like:
if (foo)
return bar;
else
return baz;
This form of a test then return is at least as readable as
if (foo)
return bar;
return baz;
so don't emit a warning on the first form.
Signed-off-by: Joe Perches <[email protected]>
Reported-by: Al Viro <[email protected]>
Cc: Elshad Mustafayev <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>