2021-11-18 10:24:06

by Wen Gong

[permalink] [raw]
Subject: [PATCH v2] ath11k: report rssi of each chain to mac80211

Command "iw wls1 station dump" does not show each chain's rssi currently.

This patch is to change like this:
If the rssi of each chain from mon status is invalid, then ath11k send
wmi cmd WMI_REQUEST_STATS_CMDID with flag WMI_REQUEST_RSSI_PER_CHAIN_STAT
to firmware, and parse the rssi of chain in wmi WMI_UPDATE_STATS_EVENTID,
then report them to mac80211.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01230-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: Wen Gong <[email protected]>
---
v2: rebased to latest ath.git master ath-202111170737

drivers/net/wireless/ath/ath11k/core.h | 7 ++
drivers/net/wireless/ath/ath11k/debugfs.c | 36 ++++++++
drivers/net/wireless/ath/ath11k/debugfs.h | 7 ++
drivers/net/wireless/ath/ath11k/dp_rx.c | 8 ++
drivers/net/wireless/ath/ath11k/hal_rx.c | 13 +++
drivers/net/wireless/ath/ath11k/hal_rx.h | 12 ++-
drivers/net/wireless/ath/ath11k/mac.c | 38 ++++++++
drivers/net/wireless/ath/ath11k/wmi.c | 107 +++++++++++++++++++++-
drivers/net/wireless/ath/ath11k/wmi.h | 14 +++
9 files changed, 240 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index bbfc10fd5c6d..12bb78f2c7a8 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -382,6 +382,7 @@ struct ath11k_sta {
u64 rx_duration;
u64 tx_duration;
u8 rssi_comb;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
struct ath11k_htt_tx_stats *tx_stats;
struct ath11k_rx_peer_stats *rx_stats;

@@ -412,6 +413,12 @@ enum ath11k_state {
/* Antenna noise floor */
#define ATH11K_DEFAULT_NOISE_FLOOR -95

+/* signed value, 11111111h, it is full bit value, invalid */
+#define ATH11K_INVALID_RSSI_FULL -1
+
+/* signed value, 10000000h, it is empty value, invalid */
+#define ATH11K_INVALID_RSSI_EMPTY -128
+
struct ath11k_fw_stats {
struct dentry *debugfs_fwstats;
u32 pdev_id;
diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c
index dba055d085be..9e3bc177a626 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
@@ -126,6 +126,11 @@ void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb
goto complete;
}

+ if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
+ ar->debug.fw_stats_done = true;
+ goto complete;
+ }
+
if (stats.stats_id == WMI_REQUEST_VDEV_STAT) {
if (list_empty(&stats.vdevs)) {
ath11k_warn(ab, "empty vdev stats");
@@ -229,6 +234,37 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
return 0;
}

+int ath11k_debug_get_fw_stats(struct ath11k *ar, u32 pdev_id, u32 vdev_id, u32 stats_id)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct stats_request_params req_param;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH11K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ req_param.pdev_id = pdev_id;
+ req_param.vdev_id = vdev_id;
+ req_param.stats_id = stats_id;
+
+ ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
+ if (ret)
+ ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
+ pdev_id, vdev_id, stats_id);
+
+err_unlock:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
{
struct ath11k *ar = inode->i_private;
diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h
index ec743a015dc7..fd71ec207786 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs.h
+++ b/drivers/net/wireless/ath/ath11k/debugfs.h
@@ -117,6 +117,7 @@ void ath11k_debugfs_unregister(struct ath11k *ar);
void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb);

void ath11k_debugfs_fw_stats_init(struct ath11k *ar);
+int ath11k_debug_get_fw_stats(struct ath11k *ar, u32 pdev_id, u32 vdev_id, u32 stats_id);

static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar)
{
@@ -216,6 +217,12 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar)
return 0;
}

+static inline int ath11k_debug_get_fw_stats(struct ath11k *ar,
+ u32 pdev_id, u32 vdev_id, u32 stats_id)
+{
+ return 0;
+}
+
#endif /* CONFIG_MAC80211_DEBUGFS*/

#endif /* _ATH11K_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index fcd7a6d27d12..85352e7a7f32 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2768,6 +2768,7 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta,
{
struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats;
u32 num_msdu;
+ int i;

if (!rx_stats)
return;
@@ -2829,6 +2830,13 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta,
rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu;

arsta->rssi_comb = ppdu_info->rssi_comb;
+
+ BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) >
+ ARRAY_SIZE(ppdu_info->rssi_chain_pri20));
+
+ for (i = 0; i < ARRAY_SIZE(arsta->chain_signal); i++)
+ arsta->chain_signal[i] = ppdu_info->rssi_chain_pri20[i];
+
rx_stats->rx_duration += ppdu_info->rx_duration;
arsta->rx_duration = rx_stats->rx_duration;
}
diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c
index 329c404cfa80..bb478771dfea 100644
--- a/drivers/net/wireless/ath/ath11k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
@@ -1080,6 +1080,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
break;
}
case HAL_PHYRX_RSSI_LEGACY: {
+ int i;
+ bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT,
+ ab->wmi_ab.svc_map);
struct hal_rx_phyrx_rssi_legacy_info *rssi =
(struct hal_rx_phyrx_rssi_legacy_info *)tlv_data;

@@ -1090,6 +1093,16 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
ppdu_info->rssi_comb =
FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB,
__le32_to_cpu(rssi->info0));
+
+ if (db2dbm) {
+ for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) {
+ u32 rssi2040 = __le32_to_cpu(rssi->preamble[i].rssi_2040);
+
+ ppdu_info->rssi_chain_pri20[i] =
+ FIELD_GET(HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20,
+ rssi2040);
+ }
+ }
break;
}
case HAL_RX_MPDU_START: {
diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h
index 0f1f04b812b9..46597b15fb33 100644
--- a/drivers/net/wireless/ath/ath11k/hal_rx.h
+++ b/drivers/net/wireless/ath/ath11k/hal_rx.h
@@ -98,6 +98,7 @@ struct hal_rx_mon_ppdu_info {
u8 ldpc;
u8 beamformed;
u8 rssi_comb;
+ u8 rssi_chain_pri20[HAL_RX_MAX_NSS];
u8 tid;
u8 dcm;
u8 ru_alloc;
@@ -248,8 +249,17 @@ struct hal_rx_he_sig_b2_ofdma_info {

#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8)

+#define HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20 GENMASK(7, 0)
+
+struct hal_rx_phyrx_chain_rssi {
+ __le32 rssi_2040;
+ __le32 rssi_80;
+} __packed;
+
struct hal_rx_phyrx_rssi_legacy_info {
- __le32 rsvd[35];
+ __le32 rsvd[3];
+ struct hal_rx_phyrx_chain_rssi pre_rssi[HAL_RX_MAX_NSS];
+ struct hal_rx_phyrx_chain_rssi preamble[HAL_RX_MAX_NSS];
__le32 info0;
} __packed;

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 292b2b7eab11..acb6a513954e 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -7481,12 +7481,42 @@ static int ath11k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
return ret;
}

+static void ath11k_mac_put_chain_rssi(struct station_info *sinfo,
+ struct ath11k_sta *arsta,
+ char *pre,
+ bool clear)
+{
+ struct ath11k *ar = arsta->arvif->ar;
+ int i;
+ s8 rssi;
+
+ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
+ sinfo->chains &= ~BIT(i);
+ rssi = arsta->chain_signal[i];
+ if (clear)
+ arsta->chain_signal[i] = ATH11K_INVALID_RSSI_FULL;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "mac sta statistics %s rssi[%d] %d\n", pre, i, rssi);
+
+ if (rssi != ATH11K_DEFAULT_NOISE_FLOOR &&
+ rssi != ATH11K_INVALID_RSSI_FULL &&
+ rssi != ATH11K_INVALID_RSSI_EMPTY &&
+ rssi != 0) {
+ sinfo->chain_signal[i] = rssi;
+ sinfo->chains |= BIT(i);
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ }
+ }
+}
+
static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+ struct ath11k *ar = arsta->arvif->ar;

sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
@@ -7509,6 +7539,14 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
}

+ ath11k_mac_put_chain_rssi(sinfo, arsta, "ppdu", false);
+
+ if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) &&
+ !ath11k_debug_get_fw_stats(ar, ar->pdev->pdev_id, 0,
+ WMI_REQUEST_RSSI_PER_CHAIN_STAT)) {
+ ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true);
+ }
+
/* TODO: Use real NF instead of default one. */
sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 614b2f6bcc8e..9c3a7c453588 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -130,6 +130,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_vdev_delete_resp_event) },
[WMI_TAG_OBSS_COLOR_COLLISION_EVT] = {
.min_len = sizeof(struct wmi_obss_color_collision_event) },
+ [WMI_TAG_PER_CHAIN_RSSI_STATS] = {
+ .min_len = sizeof(struct wmi_per_chain_rssi_stats) },
};

#define PRIMAP(_hw_mode_) \
@@ -5432,10 +5434,17 @@ ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src,
int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
struct ath11k_fw_stats *stats)
{
+ struct ath11k *ar;
const void **tb;
const struct wmi_stats_event *ev;
+ const struct wmi_per_chain_rssi_stats *rssi;
+ const struct wmi_rssi_stats *stats_rssi;
+ struct ieee80211_sta *sta;
+ struct ath11k_sta *arsta;
const void *data;
- int i, ret;
+ const struct wmi_tlv *tlv;
+ u16 tlv_tag, tlv_len;
+ int i, ret, rssi_num = 0;
u32 len = skb->len;

tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC);
@@ -5447,12 +5456,18 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,

ev = tb[WMI_TAG_STATS_EVENT];
data = tb[WMI_TAG_ARRAY_BYTE];
+ rssi = tb[WMI_TAG_PER_CHAIN_RSSI_STATS];
if (!ev || !data) {
ath11k_warn(ab, "failed to fetch update stats ev");
kfree(tb);
return -EPROTO;
}

+ if (rssi && (ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT))
+ rssi_num = rssi->num_per_chain_rssi_stats;
+
+ ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id);
+
ath11k_dbg(ab, ATH11K_DBG_WMI,
"wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n",
ev->pdev_id,
@@ -5533,6 +5548,96 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
list_add_tail(&dst->list, &stats->bcn);
}

+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi stats id 0x%x num chain %d\n",
+ ev->stats_id,
+ rssi_num);
+
+ if (rssi_num) {
+ /* This TLV of WMI_TAG_PER_CHAIN_RSSI_STATS is followed by
+ * another TLV of array of structs
+ * wmi_rssi_stats rssi_stats[num_per_chain_rssi_stats].
+ * So add check integrity for the TLVs.
+ * rssi is behind the TLV of WMI_TAG_PER_CHAIN_RSSI_STATS.
+ */
+ tlv = (struct wmi_tlv *)((u8 *)rssi - sizeof(*tlv));
+ tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
+
+ /* Skip wmi_per_chain_rssi_stats to get the TLV of array structs */
+ tlv = (struct wmi_tlv *)((u8 *)rssi + tlv_len);
+ if (((u8 *)tlv - skb->data) >= skb->len)
+ goto fin;
+
+ tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
+ if (tlv_tag != WMI_TAG_ARRAY_STRUCT)
+ rssi_num = 0;
+
+ /* Skip array struct TLV to get the array of structs */
+ tlv++;
+ if (((u8 *)tlv - skb->data) >= skb->len)
+ goto fin;
+
+ tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
+ }
+
+ for (i = 0; i < rssi_num; i++) {
+ struct ath11k_vif *arvif;
+ int j;
+
+ stats_rssi = (struct wmi_rssi_stats *)((u8 *)tlv + i *
+ (sizeof(*tlv) + tlv_len));
+ if (((u8 *)stats_rssi - skb->data) >= skb->len)
+ goto fin;
+
+ tlv_tag = FIELD_GET(WMI_TLV_TAG, stats_rssi->tlv_header);
+ if (tlv_tag != WMI_TAG_RSSI_STATS) {
+ ath11k_warn(ab, "invalid rssi stats TLV data\n");
+ break;
+ }
+
+ stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi stats vdev id %d mac %pM\n",
+ stats_rssi->vdev_id, stats_rssi->peer_macaddr.addr);
+
+ arvif = ath11k_mac_get_arvif(ar, stats_rssi->vdev_id);
+ if (!arvif) {
+ ath11k_warn(ab, "not found vif for vdev id %d\n",
+ stats_rssi->vdev_id);
+ continue;
+ }
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi stats bssid %pM vif %pK\n",
+ arvif->bssid, arvif->vif);
+
+ sta = ieee80211_find_sta_by_ifaddr(ar->hw,
+ arvif->bssid,
+ NULL);
+ if (!sta) {
+ ath11k_warn(ab, "not found station for bssid %pM\n",
+ arvif->bssid);
+ continue;
+ }
+
+ arsta = (struct ath11k_sta *)sta->drv_priv;
+
+ BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) >
+ ARRAY_SIZE(stats_rssi->rssi_avg_beacon));
+
+ for (j = 0; j < ARRAY_SIZE(arsta->chain_signal); j++) {
+ arsta->chain_signal[j] = stats_rssi->rssi_avg_beacon[j];
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi stats beacon rssi[%d] %d data rssi[%d] %d\n",
+ j,
+ stats_rssi->rssi_avg_beacon[j],
+ j,
+ stats_rssi->rssi_avg_data[j]);
+ }
+ }
+
+fin:
kfree(tb);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 4eb06cb7f883..6d703216550f 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -4394,6 +4394,20 @@ struct wmi_stats_event {
u32 num_peer_extd2_stats;
} __packed;

+#define WMI_MAX_CHAINS 8
+
+struct wmi_rssi_stats {
+ u32 tlv_header;
+ u32 vdev_id;
+ u32 rssi_avg_beacon[WMI_MAX_CHAINS];
+ u32 rssi_avg_data[WMI_MAX_CHAINS];
+ struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_per_chain_rssi_stats {
+ u32 num_per_chain_rssi_stats;
+} __packed;
+
struct wmi_pdev_ctl_failsafe_chk_event {
u32 pdev_id;
u32 ctl_failsafe_status;

base-commit: 63ec871bc50a306aac550e2d85f697ca2d5f5deb
--
2.31.1



2021-11-26 10:18:49

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2] ath11k: report rssi of each chain to mac80211

Wen Gong <[email protected]> writes:

> Command "iw wls1 station dump" does not show each chain's rssi currently.
>
> This patch is to change like this:
> If the rssi of each chain from mon status is invalid, then ath11k send
> wmi cmd WMI_REQUEST_STATS_CMDID with flag WMI_REQUEST_RSSI_PER_CHAIN_STAT
> to firmware, and parse the rssi of chain in wmi WMI_UPDATE_STATS_EVENTID,
> then report them to mac80211.

A bit more information about the design would be nice. With mon status I
guess you mean ath11k_hal_rx_parse_mon_status()? How is performance and
power consumption affected here? Especially I'm worried how often this
new WMI command is sent, is it only when ath11k_mac_op_sta_statistics()
is called?

And I think this only works when CONFIG_ATH11K_DEBUGFS is enabled,
right?

> --- a/drivers/net/wireless/ath/ath11k/core.h
> +++ b/drivers/net/wireless/ath/ath11k/core.h
> @@ -382,6 +382,7 @@ struct ath11k_sta {
> u64 rx_duration;
> u64 tx_duration;
> u8 rssi_comb;
> + s8 chain_signal[IEEE80211_MAX_CHAINS];
> struct ath11k_htt_tx_stats *tx_stats;
> struct ath11k_rx_peer_stats *rx_stats;
>
> @@ -412,6 +413,12 @@ enum ath11k_state {
> /* Antenna noise floor */
> #define ATH11K_DEFAULT_NOISE_FLOOR -95
>
> +/* signed value, 11111111h, it is full bit value, invalid */
> +#define ATH11K_INVALID_RSSI_FULL -1

The comment is really providing any value, please remove.

> +/* signed value, 10000000h, it is empty value, invalid */
> +#define ATH11K_INVALID_RSSI_EMPTY -128

Same here.

> --- a/drivers/net/wireless/ath/ath11k/debugfs.h
> +++ b/drivers/net/wireless/ath/ath11k/debugfs.h
> @@ -117,6 +117,7 @@ void ath11k_debugfs_unregister(struct ath11k *ar);
> void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb);
>
> void ath11k_debugfs_fw_stats_init(struct ath11k *ar);
> +int ath11k_debug_get_fw_stats(struct ath11k *ar, u32 pdev_id, u32 vdev_id, u32 stats_id);

ath11k_debugfs_get_fw_stats

> --- a/drivers/net/wireless/ath/ath11k/hal_rx.c
> +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
> @@ -1080,6 +1080,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
> break;
> }
> case HAL_PHYRX_RSSI_LEGACY: {
> + int i;
> + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT,
> + ab->wmi_ab.svc_map);
> struct hal_rx_phyrx_rssi_legacy_info *rssi =
> (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data;
>
> @@ -1090,6 +1093,16 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
> ppdu_info->rssi_comb =
> FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB,
> __le32_to_cpu(rssi->info0));
> +
> + if (db2dbm) {
> + for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) {
> + u32 rssi2040 = __le32_to_cpu(rssi->preamble[i].rssi_2040);
> +
> + ppdu_info->rssi_chain_pri20[i] =
> + FIELD_GET(HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20,
> + rssi2040);

le32_get_bits() makes the code simpler.

> int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
> struct ath11k_fw_stats *stats)
> {
> + struct ath11k *ar;
> const void **tb;
> const struct wmi_stats_event *ev;
> + const struct wmi_per_chain_rssi_stats *rssi;
> + const struct wmi_rssi_stats *stats_rssi;
> + struct ieee80211_sta *sta;
> + struct ath11k_sta *arsta;
> const void *data;
> - int i, ret;
> + const struct wmi_tlv *tlv;
> + u16 tlv_tag, tlv_len;
> + int i, ret, rssi_num = 0;
> u32 len = skb->len;
>
> tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC);
> @@ -5447,12 +5456,18 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
>
> ev = tb[WMI_TAG_STATS_EVENT];
> data = tb[WMI_TAG_ARRAY_BYTE];
> + rssi = tb[WMI_TAG_PER_CHAIN_RSSI_STATS];
> if (!ev || !data) {
> ath11k_warn(ab, "failed to fetch update stats ev");
> kfree(tb);
> return -EPROTO;
> }
>
> + if (rssi && (ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT))
> + rssi_num = rssi->num_per_chain_rssi_stats;
> +
> + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id);
> +
> ath11k_dbg(ab, ATH11K_DBG_WMI,
> "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n",
> ev->pdev_id,
> @@ -5533,6 +5548,96 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
> list_add_tail(&dst->list, &stats->bcn);
> }
>
> + ath11k_dbg(ab, ATH11K_DBG_WMI,
> + "wmi stats id 0x%x num chain %d\n",
> + ev->stats_id,
> + rssi_num);
> +
> + if (rssi_num) {
> + /* This TLV of WMI_TAG_PER_CHAIN_RSSI_STATS is followed by
> + * another TLV of array of structs
> + * wmi_rssi_stats rssi_stats[num_per_chain_rssi_stats].
> + * So add check integrity for the TLVs.
> + * rssi is behind the TLV of WMI_TAG_PER_CHAIN_RSSI_STATS.
> + */
> + tlv = (struct wmi_tlv *)((u8 *)rssi - sizeof(*tlv));
> + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
> +
> + /* Skip wmi_per_chain_rssi_stats to get the TLV of array structs */
> + tlv = (struct wmi_tlv *)((u8 *)rssi + tlv_len);
> + if (((u8 *)tlv - skb->data) >= skb->len)
> + goto fin;
> +
> + tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
> + if (tlv_tag != WMI_TAG_ARRAY_STRUCT)
> + rssi_num = 0;
> +
> + /* Skip array struct TLV to get the array of structs */
> + tlv++;
> + if (((u8 *)tlv - skb->data) >= skb->len)
> + goto fin;
> +
> + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
> + }
> +
> + for (i = 0; i < rssi_num; i++) {
> + struct ath11k_vif *arvif;
> + int j;
> +
> + stats_rssi = (struct wmi_rssi_stats *)((u8 *)tlv + i *
> + (sizeof(*tlv) + tlv_len));
> + if (((u8 *)stats_rssi - skb->data) >= skb->len)
> + goto fin;
> +
> + tlv_tag = FIELD_GET(WMI_TLV_TAG, stats_rssi->tlv_header);
> + if (tlv_tag != WMI_TAG_RSSI_STATS) {
> + ath11k_warn(ab, "invalid rssi stats TLV data\n");
> + break;
> + }

In this function there's a lot of pointer arithmetic and casting, can't
you use ath11k_wmi_tlv_parse_alloc() & friends for parsing the TLVs?

> --- a/drivers/net/wireless/ath/ath11k/wmi.h
> +++ b/drivers/net/wireless/ath/ath11k/wmi.h
> @@ -4394,6 +4394,20 @@ struct wmi_stats_event {
> u32 num_peer_extd2_stats;
> } __packed;
>
> +#define WMI_MAX_CHAINS 8

This is already defined on line 27.

--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2021-12-06 10:39:51

by Wen Gong

[permalink] [raw]
Subject: Re: [PATCH v2] ath11k: report rssi of each chain to mac80211

On 11/26/2021 6:16 PM, Kalle Valo wrote:
> Wen Gong <[email protected]> writes:
>
>> Command "iw wls1 station dump" does not show each chain's rssi currently.
>>
>> This patch is to change like this:
>> If the rssi of each chain from mon status is invalid, then ath11k send
>> wmi cmd WMI_REQUEST_STATS_CMDID with flag WMI_REQUEST_RSSI_PER_CHAIN_STAT
>> to firmware, and parse the rssi of chain in wmi WMI_UPDATE_STATS_EVENTID,
>> then report them to mac80211.
> A bit more information about the design would be nice. With mon status I
> guess you mean ath11k_hal_rx_parse_mon_status()? How is performance and
> power consumption affected here? Especially I'm worried how often this
> new WMI command is sent, is it only when ath11k_mac_op_sta_statistics()
> is called?

yes, it is ath11k_hal_rx_parse_mon_status().

I did a test in Ubuntu20, after STATION connected to AP, ath11k_mac_op_sta_statistics()
is only called each 6 seconds by NetworkManager in below stack.
This patch is similar with "ath10k: add rx bitrate report for SDIO"(https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git/commit/drivers/net/wireless/ath/ath10k?id=0f7cb26830a6e740455a7064e46ff1e926197ecb). And it does not
impact performance. Also the 1 wmi/event for each 6 seconds does not impact power consumption.

Also I add check similar with ath10k/SDIO only for WCN6855/QCA6390.

[  797.005587] CPU: 0 PID: 701 Comm: NetworkManager Tainted: G        W 
OE     5.13.0-rc6-wt-ath+ #2
[  797.005596] Hardware name: LENOVO 418065C/418065C, BIOS 83ET63WW
(1.33 ) 07/29/2011
[  797.005600] RIP: 0010:ath11k_mac_op_sta_statistics+0x2f/0x1b0 [ath11k]
[  797.005644] Code: 41 56 41 55 4c 8d aa 58 01 00 00 41 54 55 48 89 d5
53 48 8b 82 58 01 00 00 48 89 cb 4c 8b 70 20 49 8b 06 4c 8b a0 90 08 00
00 <0f> 0b 48 8b 82 b8 01 00 00 48 ba 00 00 00 00 01 00 00 00 48 89 81
[  797.005651] RSP: 0018:ffffb1fc80a4b890 EFLAGS: 00010282
[  797.005658] RAX: ffff8a5726200000 RBX: ffffb1fc80a4b958 RCX:
ffffb1fc80a4b958
[  797.005664] RDX: ffff8a5726a609f0 RSI: ffff8a581247f598 RDI:
ffff8a5702878800
[  797.005668] RBP: ffff8a5726a609f0 R08: 0000000000000000 R09:
0000000000000000
[  797.005672] R10: 0000000000000000 R11: 0000000000000007 R12:
02dd68024f75f480
[  797.005676] R13: ffff8a5726a60b48 R14: ffff8a5702879f40 R15:
ffff8a5726a60000
[  797.005681] FS:  00007f632c52a380(0000) GS:ffff8a583a200000(0000)
knlGS:0000000000000000
[  797.005687] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  797.005692] CR2: 00007fb025d69000 CR3: 00000001124f6005 CR4:
00000000000606f0
[  797.005698] Call Trace:
[  797.005710]  sta_set_sinfo+0xa7/0xb80 [mac80211]
[  797.005820]  ieee80211_get_station+0x50/0x70 [mac80211]
[  797.005925]  nl80211_get_station+0xd1/0x200 [cfg80211]
[  797.006045]  genl_family_rcv_msg_doit.isra.15+0x111/0x140
[  797.006059]  genl_rcv_msg+0xe6/0x1e0
[  797.006065]  ? nl80211_dump_station+0x220/0x220 [cfg80211]
[  797.006223]  ? nl80211_send_station.isra.72+0xf50/0xf50 [cfg80211]
[  797.006348]  ? genl_family_rcv_msg_doit.isra.15+0x140/0x140
[  797.006355]  netlink_rcv_skb+0xb9/0xf0
[  797.006363]  genl_rcv+0x24/0x40
[  797.006369]  netlink_unicast+0x18e/0x290
[  797.006375]  netlink_sendmsg+0x30f/0x450
[  797.006382]  sock_sendmsg+0x5b/0x60
[  797.006393]  ____sys_sendmsg+0x219/0x240
[  797.006403]  ? copy_msghdr_from_user+0x5c/0x90
[  797.006413]  ? ____sys_recvmsg+0xf5/0x190
[  797.006422]  ___sys_sendmsg+0x88/0xd0
[  797.006432]  ? copy_msghdr_from_user+0x5c/0x90
[  797.006443]  ? ___sys_recvmsg+0x9e/0xd0
[  797.006454]  ? __fget_files+0x58/0x90
[  797.006461]  ? __fget_light+0x2d/0x70
[  797.006466]  ? do_epoll_wait+0xce/0x720
[  797.006476]  ? __sys_sendmsg+0x63/0xa0
[  797.006485]  __sys_sendmsg+0x63/0xa0
[  797.006497]  do_syscall_64+0x3c/0xb0
[  797.006509]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[  797.006519] RIP: 0033:0x7f632d99912d
[  797.006526] Code: 28 89 54 24 1c 48 89 74 24 10 89 7c 24 08 e8 ca ee
ff ff 8b 54 24 1c 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 2e 00 00 00 0f
05 <48> 3d 00 f0 ff ff 77 2f 44 89 c7 48 89 44 24 08 e8 fe ee ff ff 48
[  797.006533] RSP: 002b:00007ffd80808c00 EFLAGS: 00000293 ORIG_RAX:
000000000000002e
[  797.006540] RAX: ffffffffffffffda RBX: 0000563dab99d840 RCX:
00007f632d99912d
[  797.006545] RDX: 0000000000000000 RSI: 00007ffd80808c50 RDI:
000000000000000b
[  797.006549] RBP: 00007ffd80808c50 R08: 0000000000000000 R09:
0000000000001000
[  797.006552] R10: 0000563dab96f010 R11: 0000000000000293 R12:
0000563dab99d840
[  797.006556] R13: 0000563dabbb28c0 R14: 00007f632dad4280 R15:
0000563dabab11c0
[  797.006563] ---[ end trace c9dcf08920c9945c ]---

>
> And I think this only works when CONFIG_ATH11K_DEBUGFS is enabled,
> right?
yes, if not enable CONFIG_ATH11K_DEBUGFS, then no value get from firmware.
>> --- a/drivers/net/wireless/ath/ath11k/core.h
>> +++ b/drivers/net/wireless/ath/ath11k/core.h
>> @@ -382,6 +382,7 @@ struct ath11k_sta {
>> u64 rx_duration;
>> u64 tx_duration;
>> u8 rssi_comb;
>> + s8 chain_signal[IEEE80211_MAX_CHAINS];
>> struct ath11k_htt_tx_stats *tx_stats;
>> struct ath11k_rx_peer_stats *rx_stats;
>>
>> @@ -412,6 +413,12 @@ enum ath11k_state {
>> /* Antenna noise floor */
>> #define ATH11K_DEFAULT_NOISE_FLOOR -95
>>
>> +/* signed value, 11111111h, it is full bit value, invalid */
>> +#define ATH11K_INVALID_RSSI_FULL -1
> The comment is really providing any value, please remove.
Ok, will remove the comment
>> +/* signed value, 10000000h, it is empty value, invalid */
>> +#define ATH11K_INVALID_RSSI_EMPTY -128
> Same here.
Ok, will remove the comment
>> --- a/drivers/net/wireless/ath/ath11k/debugfs.h
>> +++ b/drivers/net/wireless/ath/ath11k/debugfs.h
>> @@ -117,6 +117,7 @@ void ath11k_debugfs_unregister(struct ath11k *ar);
>> void ath11k_debugfs_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb);
>>
>> void ath11k_debugfs_fw_stats_init(struct ath11k *ar);
>> +int ath11k_debug_get_fw_stats(struct ath11k *ar, u32 pdev_id, u32 vdev_id, u32 stats_id);
> ath11k_debugfs_get_fw_stats
will change it.
>
>> --- a/drivers/net/wireless/ath/ath11k/hal_rx.c
>> +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
>> @@ -1080,6 +1080,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
>> break;
>> }
>> case HAL_PHYRX_RSSI_LEGACY: {
>> + int i;
>> + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT,
>> + ab->wmi_ab.svc_map);
>> struct hal_rx_phyrx_rssi_legacy_info *rssi =
>> (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data;
>>
>> @@ -1090,6 +1093,16 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
>> ppdu_info->rssi_comb =
>> FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB,
>> __le32_to_cpu(rssi->info0));
>> +
>> + if (db2dbm) {
>> + for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) {
>> + u32 rssi2040 = __le32_to_cpu(rssi->preamble[i].rssi_2040);
>> +
>> + ppdu_info->rssi_chain_pri20[i] =
>> + FIELD_GET(HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20,
>> + rssi2040);
> le32_get_bits() makes the code simpler.
ok.
>
>> int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
>> struct ath11k_fw_stats *stats)
>> {
>> + struct ath11k *ar;
>> const void **tb;
>> const struct wmi_stats_event *ev;
>> + const struct wmi_per_chain_rssi_stats *rssi;
>> + const struct wmi_rssi_stats *stats_rssi;
>> + struct ieee80211_sta *sta;
>> + struct ath11k_sta *arsta;
>> const void *data;
>> - int i, ret;
>> + const struct wmi_tlv *tlv;
>> + u16 tlv_tag, tlv_len;
>> + int i, ret, rssi_num = 0;
>> u32 len = skb->len;
>>
>> tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC);
>> @@ -5447,12 +5456,18 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
>>
>> ev = tb[WMI_TAG_STATS_EVENT];
>> data = tb[WMI_TAG_ARRAY_BYTE];
>> + rssi = tb[WMI_TAG_PER_CHAIN_RSSI_STATS];
>> if (!ev || !data) {
>> ath11k_warn(ab, "failed to fetch update stats ev");
>> kfree(tb);
>> return -EPROTO;
>> }
>>
>> + if (rssi && (ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT))
>> + rssi_num = rssi->num_per_chain_rssi_stats;
>> +
>> + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id);
>> +
>> ath11k_dbg(ab, ATH11K_DBG_WMI,
>> "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n",
>> ev->pdev_id,
>> @@ -5533,6 +5548,96 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,
>> list_add_tail(&dst->list, &stats->bcn);
>> }
>>
>> + ath11k_dbg(ab, ATH11K_DBG_WMI,
>> + "wmi stats id 0x%x num chain %d\n",
>> + ev->stats_id,
>> + rssi_num);
>> +
>> + if (rssi_num) {
>> + /* This TLV of WMI_TAG_PER_CHAIN_RSSI_STATS is followed by
>> + * another TLV of array of structs
>> + * wmi_rssi_stats rssi_stats[num_per_chain_rssi_stats].
>> + * So add check integrity for the TLVs.
>> + * rssi is behind the TLV of WMI_TAG_PER_CHAIN_RSSI_STATS.
>> + */
>> + tlv = (struct wmi_tlv *)((u8 *)rssi - sizeof(*tlv));
>> + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
>> +
>> + /* Skip wmi_per_chain_rssi_stats to get the TLV of array structs */
>> + tlv = (struct wmi_tlv *)((u8 *)rssi + tlv_len);
>> + if (((u8 *)tlv - skb->data) >= skb->len)
>> + goto fin;
>> +
>> + tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
>> + if (tlv_tag != WMI_TAG_ARRAY_STRUCT)
>> + rssi_num = 0;
>> +
>> + /* Skip array struct TLV to get the array of structs */
>> + tlv++;
>> + if (((u8 *)tlv - skb->data) >= skb->len)
>> + goto fin;
>> +
>> + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
>> + }
>> +
>> + for (i = 0; i < rssi_num; i++) {
>> + struct ath11k_vif *arvif;
>> + int j;
>> +
>> + stats_rssi = (struct wmi_rssi_stats *)((u8 *)tlv + i *
>> + (sizeof(*tlv) + tlv_len));
>> + if (((u8 *)stats_rssi - skb->data) >= skb->len)
>> + goto fin;
>> +
>> + tlv_tag = FIELD_GET(WMI_TLV_TAG, stats_rssi->tlv_header);
>> + if (tlv_tag != WMI_TAG_RSSI_STATS) {
>> + ath11k_warn(ab, "invalid rssi stats TLV data\n");
>> + break;
>> + }
> In this function there's a lot of pointer arithmetic and casting, can't
> you use ath11k_wmi_tlv_parse_alloc() & friends for parsing the TLVs?
will change like ath11k_wmi_tlv_dma_buf_parse().
>> --- a/drivers/net/wireless/ath/ath11k/wmi.h
>> +++ b/drivers/net/wireless/ath/ath11k/wmi.h
>> @@ -4394,6 +4394,20 @@ struct wmi_stats_event {
>> u32 num_peer_extd2_stats;
>> } __packed;
>>
>> +#define WMI_MAX_CHAINS 8
> This is already defined on line 27.
>