Skip to contentSkip to conversation

mac80211: Fix station rx-packets counters.

PATCH In review [email protected] 1 participant 7 messages Mar 24, 2021 – Apr 1, 2021

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/6 mac80211: Fix station rx-packets counters.

From: Ben Greear <[email protected]>

I noticed 'iw dev wlan6 station dump' showed almost no rx-packets one one of my radios. The rx-amsdu path did not appear to gather any stats, and after code inspection, neither did the rx-data handler.

Add common method to deal with these stats. Verified in AX and /a mode, stats look at least generally correct now.

---
 net/mac80211/rx.c | 54 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 17 deletions(-)
net/mac80211/rx.c +37 -17
index eb8225209005..4a64c2183a27 100644
@@ -1713,6 +1713,27 @@ ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx)
 	return RX_CONTINUE;
 }
 
+static void ieee80211_update_data_rx_stats(struct ieee80211_rx_data *rx,
+					   struct ieee80211_sta_rx_stats *stats,
+					   struct ieee80211_rx_status *status,
+					   int skb_len)
+{
+	stats->fragments++;
+	stats->packets++;
+	stats->last_rx = jiffies;
+	stats->last_rate = sta_stats_encode_rate(status);
+
+	/* The seqno index has the same property as needed
+	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+	 * for non-QoS-data frames. Here we know it's a data
+	 * frame, so count MSDUs.
+	 */
+	u64_stats_update_begin(&stats->syncp);
+	stats->msdu[rx->seqno_idx]++;
+	stats->bytes += skb_len;
+	u64_stats_update_end(&stats->syncp);
+}
+
 static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 {
@@ -2706,6 +2727,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	__le16 fc = hdr->frame_control;
+	ieee80211_rx_result rv;
+	int orig_len = skb->len;
 
 	if (!(status->rx_flags & IEEE80211_RX_AMSDU))
 		return RX_CONTINUE;
@@ -2734,7 +2757,12 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
 	if (is_multicast_ether_addr(hdr->addr1))
 		return RX_DROP_UNUSABLE;
 
-	return __ieee80211_rx_h_amsdu(rx, 0);
+	rv = __ieee80211_rx_h_amsdu(rx, 0);
+	if ((rv == RX_QUEUED) && (rx->sta)) {
+		struct ieee80211_sta_rx_stats *stats = &rx->sta->rx_stats;
+		ieee80211_update_data_rx_stats(rx, stats, status, orig_len);
+	}
+	return rv;
 }
 
 #ifdef CONFIG_MAC80211_MESH
@@ -2958,6 +2986,13 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 		mod_timer(&local->dynamic_ps_timer, jiffies +
 			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
 
+	
+	if (rx->sta) {
+		struct ieee80211_sta_rx_stats *stats = &rx->sta->rx_stats;
+		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+		ieee80211_update_data_rx_stats(rx, stats, status, rx->skb->len);
+	}
+
 	ieee80211_deliver_skb(rx);
 
 	return RX_QUEUED;
@@ -4400,12 +4435,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
 		return true;
 	}
 
-	stats->last_rx = jiffies;
-	stats->last_rate = sta_stats_encode_rate(status);
-
-	stats->fragments++;
-	stats->packets++;
-
 	/* do the header conversion - first grab the addresses */
 	ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs);
 	ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs);
@@ -4416,18 +4445,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
 
 	skb->dev = fast_rx->dev;
 
+	ieee80211_update_data_rx_stats(rx, stats, status, orig_len);
 	dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
 
-	/* The seqno index has the same property as needed
-	 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
-	 * for non-QoS-data frames. Here we know it's a data
-	 * frame, so count MSDUs.
-	 */
-	u64_stats_update_begin(&stats->syncp);
-	stats->msdu[rx->seqno_idx]++;
-	stats->bytes += orig_len;
-	u64_stats_update_end(&stats->syncp);
-
 	if (fast_rx->internal_forward) {
 		struct sk_buff *xmit_skb = NULL;
 		if (is_multicast_ether_addr(addrs.da)) {
Signature · 1 line
2.20.1
#1 lore raw

PATCH v2 2/6 mac80211: Provide per-station stats in debugfs

From: Ben Greear <[email protected]>

Including per tid and per acs stats.

Nice for those who like to peer deep into the guts of a system.

---
 net/mac80211/debugfs_sta.c | 88 ++++++++++++++++++++++++++++++++++++++
 net/mac80211/sta_info.c    | 33 ++++++++++++++
 net/mac80211/sta_info.h    |  4 ++
 3 files changed, 125 insertions(+)
net/mac80211/debugfs_sta.c +88 -0
index 1deacce85177..374db61527a9 100644
@@ -102,6 +102,93 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
 }
 STA_OPS(flags);
 
+static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
+			      size_t count, loff_t *ppos)
+{
+	struct sta_info *sta = file->private_data;
+	unsigned int len = 0;
+	const int buf_len = 8000;
+	char *buf = kzalloc(buf_len, GFP_KERNEL);
+	unsigned long sum;
+	char tmp[60];
+	int i;
+	struct ieee80211_sta_rx_stats rx_stats = {0};
+
+	if (!buf)
+		return -ENOMEM;
+
+	sta_accum_rx_stats(sta, &rx_stats);
+
+#define PRINT_MY_STATS(a, b) do {					\
+		len += scnprintf(buf + len, buf_len - len, "%30s %18lu\n", a, (unsigned long)(b)); \
+		if (len >= buf_len) {					\
+			goto done;					\
+		}							\
+	} while (0)
+
+#define PRINT_MY_STATS_S(a, b) do {					\
+		len += scnprintf(buf + len, buf_len - len, "%30s %18ld\n", a, (long)(b)); \
+		if (len >= buf_len) {					\
+			goto done;					\
+		}							\
+	} while (0)
+
+	PRINT_MY_STATS("rx-packets", rx_stats.packets);
+	PRINT_MY_STATS("rx-bytes", rx_stats.bytes);
+	PRINT_MY_STATS("rx-dup", rx_stats.num_duplicates);
+	PRINT_MY_STATS("rx-fragments", rx_stats.fragments);
+	PRINT_MY_STATS("rx-dropped", rx_stats.dropped);
+	PRINT_MY_STATS_S("rx-last-signal", rx_stats.last_signal);
+
+	for (i = 0; i<IEEE80211_MAX_CHAINS; i++) {
+		if (rx_stats.chains & (1<<i)) {
+			sprintf(tmp, "rx-last-signal-chain[%i]", i);
+			PRINT_MY_STATS_S(tmp, rx_stats.chain_signal_last[i]);
+		}
+	}
+	PRINT_MY_STATS("rx-last-rate-encoded", rx_stats.last_rate);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+
+	sum = sta->tx_stats.packets[0] + sta->tx_stats.packets[1]
+		+ sta->tx_stats.packets[2] + sta->tx_stats.packets[3];
+	PRINT_MY_STATS("tx-packets", sum);
+
+		sum = sta->tx_stats.bytes[0] + sta->tx_stats.bytes[1]
+		+ sta->tx_stats.bytes[2] + sta->tx_stats.bytes[3];
+	PRINT_MY_STATS("tx-bytes", sum);
+
+	/* per txq stats */
+	PRINT_MY_STATS("tx-packets-acs[VO]", sta->tx_stats.packets[IEEE80211_AC_VO]);
+	PRINT_MY_STATS("tx-packets-acs[VI]", sta->tx_stats.packets[IEEE80211_AC_VI]);
+	PRINT_MY_STATS("tx-packets-acs[BE]", sta->tx_stats.packets[IEEE80211_AC_BE]);
+	PRINT_MY_STATS("tx-packets-acs[BK]", sta->tx_stats.packets[IEEE80211_AC_BK]);
+
+	PRINT_MY_STATS("tx-bytes-acs[VO]", sta->tx_stats.bytes[IEEE80211_AC_VO]);
+	PRINT_MY_STATS("tx-bytes-acs[VI]", sta->tx_stats.bytes[IEEE80211_AC_VI]);
+	PRINT_MY_STATS("tx-bytes-acs[BE]", sta->tx_stats.bytes[IEEE80211_AC_BE]);
+	PRINT_MY_STATS("tx-bytes-acs[BK]", sta->tx_stats.bytes[IEEE80211_AC_BK]);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	for (i = 0; i<=IEEE80211_NUM_TIDS; i++) {
+		sprintf(tmp, "tx-msdu-tid[%2i]", i);
+		PRINT_MY_STATS(tmp, sta->tx_stats.msdu[i]);
+	}
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	for (i = 0; i<=IEEE80211_NUM_TIDS; i++) {
+		sprintf(tmp, "rx-msdu-tid[%2i]", i);
+		PRINT_MY_STATS(tmp, rx_stats.msdu[i]);
+	}
+
+#undef PRINT_MY_STATS
+done:
+	i = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+	kfree(buf);
+	return i;
+}
+STA_OPS(stats);
+
 static ssize_t sta_num_ps_buf_frames_read(struct file *file,
 					  char __user *userbuf,
 					  size_t count, loff_t *ppos)
@@ -1073,6 +1160,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
 	sta->debugfs_dir = debugfs_create_dir(mac, stations_dir);
 
 	DEBUGFS_ADD(flags);
+	DEBUGFS_ADD(stats);
 	DEBUGFS_ADD(aid);
 	DEBUGFS_ADD(num_ps_buf_frames);
 	DEBUGFS_ADD(last_seq_ctrl);
net/mac80211/sta_info.c +33 -0
index b096370b45b1..aa95db547465 100644
@@ -2650,6 +2650,39 @@ static void sta_update_codel_params(struct sta_info *sta, u32 thr)
 	}
 }
 
+void sta_accum_rx_stats(struct sta_info *sta,
+			struct ieee80211_sta_rx_stats *rx_stats)
+{
+	int cpu;
+	int i;
+
+	memcpy(rx_stats, &sta->rx_stats, sizeof(*rx_stats));
+
+	if (!sta->pcpu_rx_stats)
+		return;
+
+	for_each_possible_cpu(cpu) {
+		struct ieee80211_sta_rx_stats *cpurxs;
+
+		cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
+		rx_stats->packets += cpurxs->packets;
+		if (time_after(cpurxs->last_rx, rx_stats->last_rx)) {
+			rx_stats->last_rx = cpurxs->last_rx;
+			rx_stats->last_signal = cpurxs->last_signal;
+			for (i = 0; i<IEEE80211_MAX_CHAINS; i++)
+				rx_stats->chain_signal_last[i] = cpurxs->chain_signal_last[i];
+			rx_stats->last_rate = cpurxs->last_rate;
+		}
+		rx_stats->num_duplicates += cpurxs->num_duplicates;
+		rx_stats->fragments += cpurxs->fragments;
+		rx_stats->dropped += cpurxs->dropped;
+		rx_stats->bytes += sta_get_stats_bytes(cpurxs);
+		for (i = 0; i<=IEEE80211_NUM_TIDS; i++) {
+			rx_stats->msdu[i] += sta_get_tidstats_msdu(cpurxs, i);
+		}
+	}
+}
+
 void ieee80211_sta_set_expected_throughput(struct ieee80211_sta *pubsta,
 					   u32 thr)
 {
net/mac80211/sta_info.h +4 -0
index 897b4d12103e..a6b13d749ffa 100644
@@ -422,6 +422,7 @@ struct mesh_sta {
 
 DECLARE_EWMA(signal, 10, 8)
 
+/* Update sta_accum_rx_stats if you change this structure. */
 struct ieee80211_sta_rx_stats {
 	unsigned long packets;
 	unsigned long last_rx;
@@ -907,4 +908,7 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
 	return r;
 }
 
+void sta_accum_rx_stats(struct sta_info *sta,
+			struct ieee80211_sta_rx_stats *rx_stats);
+
 #endif /* STA_INFO_H */
Signature · 1 line
2.20.1
#2 lore raw

PATCH v2 3/6 mac80211: Provide detailed station rx stats.

In reply to [email protected]

From: Ben Greear <[email protected]>

This provides histograms of different encoding types, nss, rate-idx, and some ofdma related stats. The goal is general visibility into what is going on with rate control, ofdma, etc.

---
 include/uapi/linux/nl80211.h |  1 +
 net/mac80211/Kconfig         | 11 ++++++++
 net/mac80211/debugfs_sta.c   | 30 ++++++++++++++++++++
 net/mac80211/rx.c            | 55 ++++++++++++++++++++++++++++++++++++
 net/mac80211/sta_info.c      | 16 +++++++++++
 net/mac80211/sta_info.h      | 17 +++++++++++
 6 files changed, 130 insertions(+)
include/uapi/linux/nl80211.h +1 -0
index 55e7be30a930..8f548a599134 100644
@@ -3269,6 +3269,7 @@ enum nl80211_he_ru_alloc {
 	NL80211_RATE_INFO_HE_RU_ALLOC_484,
 	NL80211_RATE_INFO_HE_RU_ALLOC_996,
 	NL80211_RATE_INFO_HE_RU_ALLOC_2x996,
+	NL80211_RATE_INFO_HE_RU_ALLOC_LAST /* new entries before this */
 };
 
 /**
net/mac80211/Kconfig +11 -0
index 51ec8256b7fa..b0ebb64b3950 100644
@@ -295,6 +295,17 @@ config MAC80211_DEBUG_COUNTERS
 
 	  If unsure, say N.
 
+config MAC80211_DEBUG_STA_COUNTERS
+	bool "Extra Station TX/RX statistics"
+	depends on MAC80211_DEBUG_MENU
+	depends on MAC80211_DEBUGFS
+	help
+	  Selecting this option causes mac80211 to keep additional
+	  and very verbose station-specific TX and RX statistics
+	  These will be exposed in debugfs.
+
+	  If unsure, say N.
+
 config MAC80211_STA_HASH_MAX_SIZE
 	int "Station hash table maximum size" if MAC80211_DEBUG_MENU
 	default 0
net/mac80211/debugfs_sta.c +30 -0
index 374db61527a9..f29e937aec5c 100644
@@ -181,6 +181,36 @@ static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
 		PRINT_MY_STATS(tmp, rx_stats.msdu[i]);
 	}
 
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+	PRINT_MY_STATS("rx-bw-20", rx_stats.msdu_20);
+	PRINT_MY_STATS("rx-bw-40", rx_stats.msdu_40);
+	PRINT_MY_STATS("rx-bw-80", rx_stats.msdu_80);
+	PRINT_MY_STATS("rx-bw-160", rx_stats.msdu_160);
+
+	PRINT_MY_STATS("rx-he-total", rx_stats.msdu_he_tot);
+	PRINT_MY_STATS("rx-vht", rx_stats.msdu_vht);
+	PRINT_MY_STATS("rx-ht", rx_stats.msdu_ht);
+	PRINT_MY_STATS("rx-legacy", rx_stats.msdu_legacy);
+
+	PRINT_MY_STATS("rx-he-ru-alloc[   26]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_26]);
+	PRINT_MY_STATS("rx-he-ru-alloc[   52]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_52]);
+	PRINT_MY_STATS("rx-he-ru-alloc[  106]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_106]);
+	PRINT_MY_STATS("rx-he-ru-alloc[  242]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_242]);
+	PRINT_MY_STATS("rx-he-ru-alloc[  484]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_484]);
+	PRINT_MY_STATS("rx-he-ru-alloc[  996]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_996]);
+	PRINT_MY_STATS("rx-he-ru-alloc[2x996]", rx_stats.msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_2x996]);
+
+	for (i = 0; i < ARRAY_SIZE(rx_stats.msdu_nss); i++) {
+		sprintf(tmp, "rx-msdu-nss[%i]", i);
+		PRINT_MY_STATS(tmp, rx_stats.msdu_nss[i]);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(rx_stats.msdu_rate_idx); i++) {
+		sprintf(tmp, "rx-rate-idx[%3i]", i);
+		PRINT_MY_STATS(tmp, rx_stats.msdu_rate_idx[i]);
+	}
+#endif
+
 #undef PRINT_MY_STATS
 done:
 	i = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
net/mac80211/rx.c +55 -0

67 lines not on this page · raw message

net/mac80211/sta_info.c +16 -0
index aa95db547465..765993802fec 100644
@@ -2680,6 +2680,22 @@ void sta_accum_rx_stats(struct sta_info *sta,
 		for (i = 0; i<=IEEE80211_NUM_TIDS; i++) {
 			rx_stats->msdu[i] += sta_get_tidstats_msdu(cpurxs, i);
 		}
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+		rx_stats->msdu_20 += cpurxs->msdu_20;
+		rx_stats->msdu_40 += cpurxs->msdu_40;
+		rx_stats->msdu_80 += cpurxs->msdu_80;
+		rx_stats->msdu_160 += cpurxs->msdu_160;
+		for (i = 0; i<NL80211_RATE_INFO_HE_RU_ALLOC_LAST; i++)
+			rx_stats->msdu_he_ru_alloc[i] += cpurxs->msdu_he_ru_alloc[i];
+		rx_stats->msdu_he_tot += cpurxs->msdu_he_tot;
+		rx_stats->msdu_vht += cpurxs->msdu_vht;
+		rx_stats->msdu_ht += cpurxs->msdu_ht;
+		rx_stats->msdu_legacy += cpurxs->msdu_legacy;
+		for (i = 0; i < ARRAY_SIZE(rx_stats->msdu_nss); i++)
+			rx_stats->msdu_nss[i] += cpurxs->msdu_nss[i];
+		for (i = 0; i < ARRAY_SIZE(rx_stats->msdu_rate_idx); i++)
+			rx_stats->msdu_rate_idx[i] += cpurxs->msdu_rate_idx[i];
+#endif
 	}
 }
 
net/mac80211/sta_info.h +17 -0
index a6b13d749ffa..e205a8a06ae7 100644
@@ -436,6 +436,23 @@ struct ieee80211_sta_rx_stats {
 	struct u64_stats_sync syncp;
 	u64 bytes;
 	u64 msdu[IEEE80211_NUM_TIDS + 1];
+
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+	/* these take liberty with how things are defined, and are
+	 * designed to give a rough idea of how things are going.
+	 */
+	u32 msdu_20;
+	u32 msdu_40;
+	u32 msdu_80;
+	u32 msdu_160;
+	u32 msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_LAST];
+	u32 msdu_he_tot;
+	u32 msdu_vht;
+	u32 msdu_ht;
+	u32 msdu_legacy;
+	u32 msdu_nss[8];
+	u32 msdu_rate_idx[13];
+#endif
 };
 
 /*
Signature · 1 line
2.20.1
#3 lore raw

PATCH v2 4/6 mac80211: Add counters for specific HE encoding types.

In reply to [email protected]

From: Ben Greear <[email protected]>

So we can get counters of SU, SU-EXT, MU, Trigger frame types. Drivers use a convoluted way to pass this up the stack, encoding the info in radiotap headers prepended on the skb. So, do a bit of hopefully-correct math to find the radiotap_he header again in mac80211 and store it for stats gathering. This only applies if you have enabled verbose mac80211 station stats.

---
 net/mac80211/debugfs_sta.c |  4 ++++
 net/mac80211/ieee80211_i.h |  5 +++++
 net/mac80211/rx.c          | 39 +++++++++++++++++++++++++++++++++-----
 net/mac80211/sta_info.c    |  4 ++++
 net/mac80211/sta_info.h    |  4 ++++
 5 files changed, 51 insertions(+), 5 deletions(-)
net/mac80211/debugfs_sta.c +4 -0
index f29e937aec5c..9dd29fe94bde 100644
@@ -188,6 +188,10 @@ static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
 	PRINT_MY_STATS("rx-bw-160", rx_stats.msdu_160);
 
 	PRINT_MY_STATS("rx-he-total", rx_stats.msdu_he_tot);
+	PRINT_MY_STATS("rx-he-su", rx_stats.msdu_he_su);
+	PRINT_MY_STATS("rx-he-ext-su", rx_stats.msdu_he_ext_su);
+	PRINT_MY_STATS("rx-he-mu", rx_stats.msdu_he_mu);
+	PRINT_MY_STATS("rx-he-trigger", rx_stats.msdu_he_trigger);
 	PRINT_MY_STATS("rx-vht", rx_stats.msdu_vht);
 	PRINT_MY_STATS("rx-ht", rx_stats.msdu_ht);
 	PRINT_MY_STATS("rx-legacy", rx_stats.msdu_legacy);
net/mac80211/ieee80211_i.h +5 -0
index 7951c6377fc8..b75a2ae5f2e9 100644
@@ -244,6 +244,11 @@ struct ieee80211_rx_data {
 
 	u32 tkip_iv32;
 	u16 tkip_iv16;
+
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+	/* for stats gathering */
+	struct ieee80211_radiotap_he radiotap_he;
+#endif
 };
 
 struct ieee80211_csa_settings {
net/mac80211/rx.c +34 -5
index 01117e2d1f27..c83428b5bb5a 100644
@@ -735,8 +735,8 @@ ieee80211_make_monitor_skb(struct ieee80211_local *local,
  * radiotap header the driver might have added.
  */
 static struct sk_buff *
-ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
-		     struct ieee80211_rate *rate)
+ieee80211_rx_monitor(struct ieee80211_radiotap_he *radiotap_he, struct ieee80211_local *local,
+		     struct sk_buff *origskb, struct ieee80211_rate *rate)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
 	struct ieee80211_sub_if_data *sdata;
@@ -796,6 +796,19 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 		return NULL;
 	}
 
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+	if (status->flag & RX_FLAG_RADIOTAP_HE) {
+		/* Store this for later so we can gather stats.
+		 * This depends on drivers putting the radiotap_he header
+		 * on the skb first.  Seems all drivers do at this point.
+		 */
+		struct ieee80211_radiotap_he *he;
+		he = (struct ieee80211_radiotap_he *)(origskb->data + (rtap_space - sizeof(*he)));
+		*radiotap_he = *he;
+	}
+#endif
+
+
 	only_monitor = should_drop_frame(origskb, present_fcs_len, rtap_space);
 
 	if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
@@ -1763,6 +1776,17 @@ static void ieee80211_update_data_rx_stats(struct ieee80211_rx_data *rx,
 
 	if (status->encoding == RX_ENC_HE) {
 		stats->msdu_he_tot++;
+		if (status->flag & RX_FLAG_RADIOTAP_HE) {
+			u8 he_type = rx->radiotap_he.data1 & 0x3;
+			if (he_type == 0x0)
+				stats->msdu_he_su++;
+			if (he_type == 0x1)
+				stats->msdu_he_ext_su++;
+			if (he_type == 0x2)
+				stats->msdu_he_mu++;
+			if (he_type == 0x3)
+				stats->msdu_he_trigger++;
+		}
 	}
 	else if (status->encoding == RX_ENC_VHT) {
 		stats->msdu_vht++;
@@ -4601,7 +4625,8 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 					 struct ieee80211_sta *pubsta,
 					 struct sk_buff *skb,
-					 struct list_head *list)
+					 struct list_head *list,
+					 struct ieee80211_radiotap_he *radiotap_he)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
@@ -4617,6 +4642,9 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	rx.skb = skb;
 	rx.local = local;
 	rx.list = list;
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+	rx.radiotap_he = *radiotap_he;
+#endif
 
 	if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
 		I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -4748,6 +4776,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	struct ieee80211_rate *rate = NULL;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_radiotap_he radiotap_he;
 
 	WARN_ON_ONCE(softirq_count() == 0);
 
@@ -4841,13 +4870,13 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	 * if it was previously present.
 	 * Also, frames with less than 16 bytes are dropped.
 	 */
-	skb = ieee80211_rx_monitor(local, skb, rate);
+	skb = ieee80211_rx_monitor(&radiotap_he, local, skb, rate);
 	if (skb) {
 		ieee80211_tpt_led_trig_rx(local,
 					  ((struct ieee80211_hdr *)skb->data)->frame_control,
 					  skb->len);
 
-		__ieee80211_rx_handle_packet(hw, pubsta, skb, list);
+		__ieee80211_rx_handle_packet(hw, pubsta, skb, list, &radiotap_he);
 	}
 
 	kcov_remote_stop();
net/mac80211/sta_info.c +4 -0
index 765993802fec..6dd62ba40133 100644
@@ -2688,6 +2688,10 @@ void sta_accum_rx_stats(struct sta_info *sta,
 		for (i = 0; i<NL80211_RATE_INFO_HE_RU_ALLOC_LAST; i++)
 			rx_stats->msdu_he_ru_alloc[i] += cpurxs->msdu_he_ru_alloc[i];
 		rx_stats->msdu_he_tot += cpurxs->msdu_he_tot;
+		rx_stats->msdu_he_su += cpurxs->msdu_he_su;
+		rx_stats->msdu_he_ext_su += cpurxs->msdu_he_ext_su;
+		rx_stats->msdu_he_mu += cpurxs->msdu_he_mu;
+		rx_stats->msdu_he_trigger += cpurxs->msdu_he_trigger;
 		rx_stats->msdu_vht += cpurxs->msdu_vht;
 		rx_stats->msdu_ht += cpurxs->msdu_ht;
 		rx_stats->msdu_legacy += cpurxs->msdu_legacy;
net/mac80211/sta_info.h +4 -0
index e205a8a06ae7..90fcebf199ad 100644
@@ -447,6 +447,10 @@ struct ieee80211_sta_rx_stats {
 	u32 msdu_160;
 	u32 msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_LAST];
 	u32 msdu_he_tot;
+	u32 msdu_he_su;
+	u32 msdu_he_ext_su;
+	u32 msdu_he_mu;
+	u32 msdu_he_trigger;
 	u32 msdu_vht;
 	u32 msdu_ht;
 	u32 msdu_legacy;
Signature · 1 line
2.20.1
#4 lore raw

PATCH v2 5/6 mac80211: Add some additional tx-stats to debugfs

In reply to [email protected]

From: Ben Greear <[email protected]>

Give a better idea of TX behaviour. Currently this code does not deal with HE tx rates properly. I'm not sure exactly where to find that info, not sure that ax200 driver that I'm testing properly provides it up to the tx-status logic.

---
 net/mac80211/debugfs_sta.c | 24 ++++++++++++++++++-
 net/mac80211/rx.c          |  1 +
 net/mac80211/sta_info.h    | 16 +++++++++++++
 net/mac80211/status.c      | 47 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
net/mac80211/debugfs_sta.c +23 -1
index 9dd29fe94bde..ed6380bb6270 100644
@@ -107,7 +107,7 @@ static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
 {
 	struct sta_info *sta = file->private_data;
 	unsigned int len = 0;
-	const int buf_len = 8000;
+	const int buf_len = 12000;
 	char *buf = kzalloc(buf_len, GFP_KERNEL);
 	unsigned long sum;
 	char tmp[60];
@@ -186,6 +186,7 @@ static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
 	PRINT_MY_STATS("rx-bw-40", rx_stats.msdu_40);
 	PRINT_MY_STATS("rx-bw-80", rx_stats.msdu_80);
 	PRINT_MY_STATS("rx-bw-160", rx_stats.msdu_160);
+	PRINT_MY_STATS("rx-bw-he-ru", rx_stats.msdu_he_ru);
 
 	PRINT_MY_STATS("rx-he-total", rx_stats.msdu_he_tot);
 	PRINT_MY_STATS("rx-he-su", rx_stats.msdu_he_su);
@@ -213,6 +214,27 @@ static ssize_t sta_stats_read(struct file *file, char __user *userbuf,
 		sprintf(tmp, "rx-rate-idx[%3i]", i);
 		PRINT_MY_STATS(tmp, rx_stats.msdu_rate_idx[i]);
 	}
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	PRINT_MY_STATS("tx-bw-20", sta->tx_stats.msdu_20);
+	PRINT_MY_STATS("tx-bw-40", sta->tx_stats.msdu_40);
+	PRINT_MY_STATS("tx-bw-80", sta->tx_stats.msdu_80);
+	PRINT_MY_STATS("tx-bw-160", sta->tx_stats.msdu_160);
+	PRINT_MY_STATS("tx-bw-he-ru", sta->tx_stats.msdu_he_ru);
+
+	PRINT_MY_STATS("tx-vht", sta->tx_stats.msdu_vht);
+	PRINT_MY_STATS("tx-ht", sta->tx_stats.msdu_ht);
+	PRINT_MY_STATS("tx-legacy", sta->tx_stats.msdu_legacy);
+
+	for (i = 0; i < ARRAY_SIZE(sta->tx_stats.msdu_nss); i++) {
+		sprintf(tmp, "tx-msdu-nss[%i]", i);
+		PRINT_MY_STATS(tmp, sta->tx_stats.msdu_nss[i]);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sta->tx_stats.msdu_rate_idx); i++) {
+		sprintf(tmp, "tx-rate-idx[%3i]", i);
+		PRINT_MY_STATS(tmp, sta->tx_stats.msdu_rate_idx[i]);
+	}
 #endif
 
 #undef PRINT_MY_STATS
net/mac80211/rx.c +1 -0
index c83428b5bb5a..be05abc77292 100644
@@ -1767,6 +1767,7 @@ static void ieee80211_update_data_rx_stats(struct ieee80211_rx_data *rx,
 		stats->msdu_160++;
 		break;
 	case RATE_INFO_BW_HE_RU:
+		stats->msdu_he_ru++;
 		stats->msdu_he_ru_alloc[status->he_ru]++;
 		break;
 	};
net/mac80211/sta_info.h +16 -0
index 90fcebf199ad..6fc6ad6248e6 100644
@@ -445,6 +445,7 @@ struct ieee80211_sta_rx_stats {
 	u32 msdu_40;
 	u32 msdu_80;
 	u32 msdu_160;
+	u32 msdu_he_ru;
 	u32 msdu_he_ru_alloc[NL80211_RATE_INFO_HE_RU_ALLOC_LAST];
 	u32 msdu_he_tot;
 	u32 msdu_he_su;
@@ -636,6 +637,21 @@ struct sta_info {
 		struct ieee80211_tx_rate last_rate;
 		struct rate_info last_rate_info;
 		u64 msdu[IEEE80211_NUM_TIDS + 1];
+#ifdef CONFIG_MAC80211_DEBUG_STA_COUNTERS
+		/* these take liberty with how things are defined, and are
+		 * designed to give a rough idea of how things are going.
+		 */
+		u32 msdu_20;
+		u32 msdu_40;
+		u32 msdu_80;
+		u32 msdu_160;
+		u32 msdu_he_ru;
+		u32 msdu_vht;
+		u32 msdu_ht;
+		u32 msdu_legacy;
+		u32 msdu_nss[8];
+		u32 msdu_rate_idx[13];
+#endif
 	} tx_stats;
 	u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
 
net/mac80211/status.c +47 -0

70 lines not on this page · raw message

Signature · 1 line
2.20.1
#5 lore raw

PATCH v2 6/6 mac80211: last_rate is 32-bit number.

In reply to [email protected]

From: Ben Greear <[email protected]>

Assign it to 32-bit holding variable instead of 16 when processing the rx rate.

---
 net/mac80211/sta_info.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/mac80211/sta_info.c +1 -1
index 6dd62ba40133..a28a562a8fb4 100644
@@ -2246,7 +2246,7 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
 
 static int sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
 {
-	u16 rate = READ_ONCE(sta_get_last_rx_stats(sta)->last_rate);
+	u32 rate = READ_ONCE(sta_get_last_rx_stats(sta)->last_rate);
 
 	if (rate == STA_STATS_RATE_INVALID)
 		return -EINVAL;
Signature · 1 line
2.20.1
author
#6 lore raw
In reply to [email protected]
Quoted 24 lines · On 3/24/21 11:14 AM, [email protected] wrote:

From: Ben Greear <[email protected]>

I noticed 'iw dev wlan6 station dump' showed almost no rx-packets one one of my radios. The rx-amsdu path did not appear to gather any stats, and after code inspection, neither did the rx-data handler.

Add common method to deal with these stats. Verified in AX and /a mode, stats look at least generally correct now.

---

net/mac80211/rx.c +2 -0
@@ -2706,6 +2727,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
  	__le16 fc = hdr->frame_control;
+	ieee80211_rx_result rv;
+	int orig_len = skb->len;
 
  	if (!(status->rx_flags & IEEE80211_RX_AMSDU))
  		return RX_CONTINUE;
@@ -2734,7 +2757,12 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
  	if (is_multicast_ether_addr(hdr->addr1))

net/mac80211/rx.c ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) · line 2766

Quoted 10 lines
net/mac80211/rx.c +6 -1
  		return RX_DROP_UNUSABLE;
 
-	return __ieee80211_rx_h_amsdu(rx, 0);
+	rv = __ieee80211_rx_h_amsdu(rx, 0);
+	if ((rv == RX_QUEUED) && (rx->sta)) {
+		struct ieee80211_sta_rx_stats *stats = &rx->sta->rx_stats;
+		ieee80211_update_data_rx_stats(rx, stats, status, orig_len);
+	}
+	return rv;
  }

I noticed this is buggy in several ways (potential use-after-free, bogus status field).

I noticed too that upcoming changes changed some API that made later patches in this series not apply cleanly..needed a bit of re-work.

Maybe best if I wait and rebase it against 5.13 when it is ready, unless someone is actually interested enough in this to want to apply it earlier?

Thanks,
Ben

Signature · 2 lines
Ben Greear <[email protected]>
Candela Technologies Inc  http://www.candelatech.com