From eff8c76aa02275a7b325e0fa93cc349380299fde Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 8 Aug 2020 19:28:07 +0200 Subject: [PATCH] mac80211: fix spurious disconnect issues with disassoc_low_ack=1 (default) mac80211 reports a packet loss event to user space when 50 consecutive packets were not acked. On a high throughput link with long aggregates and sudden link changes, this can trigger way too easily. Mitigate false positives by only triggering the event on a packet loss if no ACK was received for at least a second Signed-off-by: Felix Fietkau --- ...ce-packet-loss-event-false-positives.patch | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 package/kernel/mac80211/patches/subsys/310-mac80211-reduce-packet-loss-event-false-positives.patch diff --git a/package/kernel/mac80211/patches/subsys/310-mac80211-reduce-packet-loss-event-false-positives.patch b/package/kernel/mac80211/patches/subsys/310-mac80211-reduce-packet-loss-event-false-positives.patch new file mode 100644 index 0000000000..85094389a8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/310-mac80211-reduce-packet-loss-event-false-positives.patch @@ -0,0 +1,116 @@ +From: Felix Fietkau +Date: Sat, 8 Aug 2020 19:20:02 +0200 +Subject: [PATCH] mac80211: reduce packet loss event false positives + +When running a large number of packets per second with a high data rate +and long A-MPDUs, the packet loss threshold can be reached very quickly +when the link conditions change. This frequently shows up as spurious +disconnects. +Mitigate false positives by using a similar logic for regular stations +as the one being used for TDLS, though with a more aggressive timeout. +Packet loss events are only reported if no ACK was received for a second. + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/sta_info.h ++++ b/net/mac80211/sta_info.h +@@ -522,7 +522,7 @@ struct ieee80211_sta_rx_stats { + * @status_stats.retry_failed: # of frames that failed after retry + * @status_stats.retry_count: # of retries attempted + * @status_stats.lost_packets: # of lost packets +- * @status_stats.last_tdls_pkt_time: timestamp of last TDLS packet ++ * @status_stats.last_pkt_time: timestamp of last ACKed packet + * @status_stats.msdu_retries: # of MSDU retries + * @status_stats.msdu_failed: # of failed MSDUs + * @status_stats.last_ack: last ack timestamp (jiffies) +@@ -595,7 +595,7 @@ struct sta_info { + unsigned long filtered; + unsigned long retry_failed, retry_count; + unsigned int lost_packets; +- unsigned long last_tdls_pkt_time; ++ unsigned long last_pkt_time; + u64 msdu_retries[IEEE80211_NUM_TIDS + 1]; + u64 msdu_failed[IEEE80211_NUM_TIDS + 1]; + unsigned long last_ack; +--- a/net/mac80211/status.c ++++ b/net/mac80211/status.c +@@ -749,12 +749,16 @@ static void ieee80211_report_used_skb(st + * - current throughput (higher value for higher tpt)? + */ + #define STA_LOST_PKT_THRESHOLD 50 ++#define STA_LOST_PKT_TIME HZ /* 1 sec since last ACK */ + #define STA_LOST_TDLS_PKT_THRESHOLD 10 + #define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */ + + static void ieee80211_lost_packet(struct sta_info *sta, + struct ieee80211_tx_info *info) + { ++ unsigned long pkt_time = STA_LOST_PKT_TIME; ++ unsigned int pkt_thr = STA_LOST_PKT_THRESHOLD; ++ + /* If driver relies on its own algorithm for station kickout, skip + * mac80211 packet loss mechanism. + */ +@@ -767,21 +771,20 @@ static void ieee80211_lost_packet(struct + return; + + sta->status_stats.lost_packets++; +- if (!sta->sta.tdls && +- sta->status_stats.lost_packets < STA_LOST_PKT_THRESHOLD) +- return; ++ if (sta->sta.tdls) { ++ pkt_time = STA_LOST_TDLS_PKT_TIME; ++ pkt_thr = STA_LOST_PKT_THRESHOLD; ++ } + + /* + * If we're in TDLS mode, make sure that all STA_LOST_TDLS_PKT_THRESHOLD + * of the last packets were lost, and that no ACK was received in the + * last STA_LOST_TDLS_PKT_TIME ms, before triggering the CQM packet-loss + * mechanism. ++ * For non-TDLS, use STA_LOST_PKT_THRESHOLD and STA_LOST_PKT_TIME + */ +- if (sta->sta.tdls && +- (sta->status_stats.lost_packets < STA_LOST_TDLS_PKT_THRESHOLD || +- time_before(jiffies, +- sta->status_stats.last_tdls_pkt_time + +- STA_LOST_TDLS_PKT_TIME))) ++ if (sta->status_stats.lost_packets < pkt_thr || ++ !time_after(jiffies, sta->status_stats.last_pkt_time + pkt_time)) + return; + + cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, +@@ -1034,9 +1037,7 @@ static void __ieee80211_tx_status(struct + sta->status_stats.lost_packets = 0; + + /* Track when last TDLS packet was ACKed */ +- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) +- sta->status_stats.last_tdls_pkt_time = +- jiffies; ++ sta->status_stats.last_pkt_time = jiffies; + } else if (noack_success) { + /* nothing to do here, do not account as lost */ + } else { +@@ -1169,9 +1170,8 @@ void ieee80211_tx_status_ext(struct ieee + if (sta->status_stats.lost_packets) + sta->status_stats.lost_packets = 0; + +- /* Track when last TDLS packet was ACKed */ +- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) +- sta->status_stats.last_tdls_pkt_time = jiffies; ++ /* Track when last packet was ACKed */ ++ sta->status_stats.last_pkt_time = jiffies; + } else if (test_sta_flag(sta, WLAN_STA_PS_STA)) { + return; + } else if (noack_success) { +@@ -1260,8 +1260,7 @@ void ieee80211_tx_status_8023(struct iee + if (sta->status_stats.lost_packets) + sta->status_stats.lost_packets = 0; + +- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) +- sta->status_stats.last_tdls_pkt_time = jiffies; ++ sta->status_stats.last_pkt_time = jiffies; + } else { + ieee80211_lost_packet(sta, info); + }