Message ID | 20211118102331.10726-1-quic_wgong@quicinc.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kalle Valo |
Headers | show |
Series | [v2] ath11k: report rssi of each chain to mac80211 | expand |
Wen Gong <quic_wgong@quicinc.com> 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.
On 11/26/2021 6:16 PM, Kalle Valo wrote: > Wen Gong <quic_wgong@quicinc.com> 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. >
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;
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 <quic_wgong@quicinc.com> --- 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(-) base-commit: 63ec871bc50a306aac550e2d85f697ca2d5f5deb