]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: 8852a: refine power save to lower latency
authorPo-Hao Huang <phhuang@realtek.com>
Mon, 20 Apr 2026 03:40:37 +0000 (11:40 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Wed, 29 Apr 2026 04:30:46 +0000 (12:30 +0800)
Improve user experience while using interactive applications.
Adjust power saving decisions under different scenarios.
Minimize latency for delay-sensitive connections by exiting
power-save on packet bursts and maintain active until a period
of inactivity is reached. For connections that are not that
sensitive to delays, keep the current aggressive power save logic.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20260420034051.17666-3-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h

index 70feab97dccb5a398d4a93e675adf84f790b8867..c9c4ec1d93affe86e2c2f5d37d1d06004519b957 100644 (file)
@@ -341,10 +341,26 @@ static const struct rtw89_hw_rate_def {
        },
 };
 
+static void rtw89_core_tx_skb_proto_stats(struct rtw89_traffic_stats *stats,
+                                         struct sk_buff *skb)
+{
+       switch (ip_hdr(skb)->protocol) {
+       case IPPROTO_TCP:
+               stats->tcp++;
+               break;
+       case IPPROTO_UDP:
+               stats->udp++;
+               break;
+       default:
+               break;
+       }
+}
+
 static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats,
                                       struct sk_buff *skb, bool tx)
 {
        if (tx) {
+               rtw89_core_tx_skb_proto_stats(stats, skb);
                stats->tx_cnt++;
                stats->tx_unicast += skb->len;
        } else {
@@ -2902,6 +2918,23 @@ static void rtw89_core_bcn_track_reset(struct rtw89_dev *rtwdev)
        memset(&rtwdev->bcn_track, 0, sizeof(rtwdev->bcn_track));
 }
 
+static bool rtw89_core_proto_stats_can_lps(struct rtw89_dev *rtwdev,
+                                          struct rtw89_vif *rtwvif,
+                                          enum rtw89_tfc_interval interval)
+{
+       if (rtwdev->chip->chip_id != RTL8852A)
+               return true;
+
+       if (rtwvif->burst_active)
+               return false;
+
+       if (interval == RTW89_TFC_INTERVAL_100MS &&
+           hweight8(rtwvif->stats_ps.active_histogram) < 3)
+               return false;
+
+       return true;
+}
+
 static void rtw89_vif_rx_bcn_stat(struct rtw89_dev *rtwdev, struct sk_buff *skb)
 {
 #define RTW89_APPEND_TSF_2GHZ 384
@@ -4540,9 +4573,42 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
        return RTW89_TFC_ULTRA_LOW;
 }
 
+static void rtw89_calc_vif_active_histogram(struct rtw89_dev *rtwdev,
+                                           struct rtw89_traffic_stats *stats,
+                                           enum rtw89_tfc_interval interval)
+{
+       struct rtw89_vif *rtwvif;
+
+       stats->udp_ratio = stats->tx_cnt ?
+                          DIV_ROUND_DOWN_ULL(stats->udp * 100, stats->tx_cnt) : 0;
+       stats->active_histogram <<= 1;
+
+       switch (interval) {
+       case RTW89_TFC_INTERVAL_2SEC:
+               rtwvif = container_of(stats, struct rtw89_vif, stats);
+
+               if (stats->tcp >= RTW89_TCP_TH && stats->tx_cnt >= stats->rx_cnt)
+                       stats->active_histogram |= BIT(0);
+
+               if (stats->active_histogram & RTW89_RECENT_ACTIVE_HIST)
+                       rtwvif->burst_active = true;
+               else
+                       rtwvif->burst_active = false;
+
+               break;
+       case RTW89_TFC_INTERVAL_100MS:
+               if (stats->udp_ratio >= RTW89_UDP_RATIO_TH)
+                       stats->active_histogram |= BIT(0);
+               break;
+       }
+
+       stats->tcp = 0;
+       stats->udp = 0;
+}
+
 static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
                                     struct rtw89_traffic_stats *stats,
-                                    enum rtw89_tfc_interval interval)
+                                    enum rtw89_tfc_interval interval, bool by_vif)
 {
        enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;
        enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv;
@@ -4564,6 +4630,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
        stats->rx_avg_len = stats->rx_cnt ?
                            DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0;
 
+       if (by_vif)
+               rtw89_calc_vif_active_histogram(rtwdev, stats, interval);
+
        stats->tx_unicast = 0;
        stats->rx_unicast = 0;
        stats->tx_cnt = 0;
@@ -4585,11 +4654,11 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
        bool tfc_changed;
 
        tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats,
-                                              RTW89_TFC_INTERVAL_2SEC);
+                                              RTW89_TFC_INTERVAL_2SEC, false);
 
        rtw89_for_each_rtwvif(rtwdev, rtwvif) {
                rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats,
-                                        RTW89_TFC_INTERVAL_2SEC);
+                                        RTW89_TFC_INTERVAL_2SEC, true);
 
                rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
                        rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link);
@@ -4598,7 +4667,8 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
        return tfc_changed;
 }
 
-static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
+static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev,
+                                 enum rtw89_tfc_interval interval)
 {
        struct ieee80211_vif *vif;
        struct rtw89_vif *rtwvif;
@@ -4622,6 +4692,9 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
                if (!rtw89_core_bcn_track_can_lps(rtwdev))
                        continue;
 
+               if (!rtw89_core_proto_stats_can_lps(rtwdev, rtwvif, interval))
+                       continue;
+
                rtw89_enter_lps(rtwdev, rtwvif, true);
        }
 }
@@ -4771,13 +4844,13 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work)
 
        rtw89_for_each_rtwvif(rtwdev, rtwvif)
                rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps,
-                                        RTW89_TFC_INTERVAL_100MS);
+                                        RTW89_TFC_INTERVAL_100MS, true);
 
        if (rtwdev->scanning)
                return;
 
        if (rtwdev->lps_enabled && !rtwdev->btc.lps)
-               rtw89_enter_lps_track(rtwdev);
+               rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS);
 }
 
 static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
@@ -4825,7 +4898,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
        rtw89_core_mlo_track(rtwdev);
 
        if (rtwdev->lps_enabled && !rtwdev->btc.lps)
-               rtw89_enter_lps_track(rtwdev);
+               rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC);
 }
 
 void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new)
index 2d7d3a11be166cb073a7eb59467b4b6f0045a01f..ea123567ee174b438a76421e99caef8853bc3eb5 100644 (file)
@@ -1477,6 +1477,10 @@ enum rtw89_tfc_lv {
 
 DECLARE_EWMA(tp, 10, 2);
 
+#define RTW89_TCP_TH 40
+#define RTW89_UDP_RATIO_TH 70
+#define RTW89_RECENT_ACTIVE_HIST GENMASK(4, 0)
+
 struct rtw89_traffic_stats {
        /* units in bytes */
        u64 tx_unicast;
@@ -1504,6 +1508,10 @@ struct rtw89_traffic_stats {
 
        u16 tx_rate;
        u16 rx_rate;
+
+       /* used by rtwvif only */
+       u64 tcp, udp, udp_ratio;
+       u8 active_histogram;
 };
 
 struct rtw89_btc_chdef {
@@ -6354,6 +6362,7 @@ struct rtw89_vif {
 
        struct rtw89_roc roc;
        bool offchan;
+       bool burst_active;
 
        enum rtw89_mlo_mode mlo_mode;
        struct rtw89_vif_ml_trans ml_trans;