]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: enter power save mode aggressively
authorChin-Yen Lee <timlee@realtek.com>
Tue, 1 Jul 2025 07:38:39 +0000 (15:38 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Fri, 4 Jul 2025 03:36:29 +0000 (11:36 +0800)
Currently the driver allows the WiFi chip enter power save mode
by checking the transmitting and receiving traffic is very low
per two seconds. But it's hard for some applications to enter
power save mode, like video streaming, which sends burst traffic
regularly for other side to buffer and only send little traffic
at most time. So adjust the criteria to enter power save while
lower than 10Mbps and check it per 100ms. Thus WiFi chip could
reduce power consumption under these applications.

Signed-off-by: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250701073839.31905-1-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/mac80211.c
drivers/net/wireless/realtek/rtw89/ser.c
drivers/net/wireless/realtek/rtw89/wow.c
drivers/net/wireless/realtek/rtw89/wow.h

index 776d2e5ce8a0b6b249591243da2a64daee7695a1..23d050041583a2fac1303f0c13fdb27993fbcd86 100644 (file)
@@ -319,15 +319,25 @@ static const struct ieee80211_supported_band rtw89_sband_6ghz = {
        .n_bitrates     = ARRAY_SIZE(rtw89_bitrates) - 4,
 };
 
+static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats,
+                                      struct sk_buff *skb, bool tx)
+{
+       if (tx) {
+               stats->tx_cnt++;
+               stats->tx_unicast += skb->len;
+       } else {
+               stats->rx_cnt++;
+               stats->rx_unicast += skb->len;
+       }
+}
+
 static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev,
-                                    struct rtw89_traffic_stats *stats,
-                                    struct sk_buff *skb, bool tx)
+                                    struct rtw89_vif *rtwvif,
+                                    struct sk_buff *skb,
+                                    bool accu_dev, bool tx)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       if (tx && ieee80211_is_assoc_req(hdr->frame_control))
-               rtw89_wow_parse_akm(rtwdev, skb);
-
        if (!ieee80211_is_data(hdr->frame_control))
                return;
 
@@ -335,12 +345,12 @@ static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev,
            is_multicast_ether_addr(hdr->addr1))
                return;
 
-       if (tx) {
-               stats->tx_cnt++;
-               stats->tx_unicast += skb->len;
-       } else {
-               stats->rx_cnt++;
-               stats->rx_unicast += skb->len;
+       if (accu_dev)
+               __rtw89_traffic_stats_accu(&rtwdev->stats, skb, tx);
+
+       if (rtwvif) {
+               __rtw89_traffic_stats_accu(&rtwvif->stats, skb, tx);
+               __rtw89_traffic_stats_accu(&rtwvif->stats_ps, skb, tx);
        }
 }
 
@@ -1150,8 +1160,8 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
        tx_req.rtwsta_link = rtwsta_link;
        tx_req.desc_info.sw_mld = sw_mld;
 
-       rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true);
-       rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true);
+       rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true);
+       rtw89_wow_parse_akm(rtwdev, skb);
        rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
        rtw89_core_tx_wake(rtwdev, &tx_req);
 
@@ -2267,7 +2277,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
        if (desc_info->data_rate < RTW89_HW_RATE_NR)
                pkt_stat->rx_rate_cnt[desc_info->data_rate]++;
 
-       rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, false);
+       rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, false, false);
 
 out:
        rcu_read_unlock();
@@ -2280,7 +2290,7 @@ static void rtw89_core_rx_stats(struct rtw89_dev *rtwdev,
 {
        struct rtw89_vif_rx_stats_iter_data iter_data;
 
-       rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, false);
+       rtw89_traffic_stats_accu(rtwdev, NULL, skb, true, false);
 
        iter_data.rtwdev = rtwdev;
        iter_data.phy_ppdu = phy_ppdu;
@@ -3570,9 +3580,22 @@ void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work)
 }
 
 static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
-                                                u32 throughput, u64 cnt)
+                                                u32 throughput, u64 cnt,
+                                                enum rtw89_tfc_interval interval)
 {
-       if (cnt < 100)
+       u64 cnt_level;
+
+       switch (interval) {
+       default:
+       case RTW89_TFC_INTERVAL_100MS:
+               cnt_level = 5;
+               break;
+       case RTW89_TFC_INTERVAL_2SEC:
+               cnt_level = 100;
+               break;
+       }
+
+       if (cnt < cnt_level)
                return RTW89_TFC_IDLE;
        if (throughput > 50)
                return RTW89_TFC_HIGH;
@@ -3584,13 +3607,14 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
 }
 
 static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
-                                    struct rtw89_traffic_stats *stats)
+                                    struct rtw89_traffic_stats *stats,
+                                    enum rtw89_tfc_interval interval)
 {
        enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;
        enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv;
 
-       stats->tx_throughput_raw = (u32)(stats->tx_unicast >> RTW89_TP_SHIFT);
-       stats->rx_throughput_raw = (u32)(stats->rx_unicast >> RTW89_TP_SHIFT);
+       stats->tx_throughput_raw = rtw89_bytes_to_mbps(stats->tx_unicast, interval);
+       stats->rx_throughput_raw = rtw89_bytes_to_mbps(stats->rx_unicast, interval);
 
        ewma_tp_add(&stats->tx_ewma_tp, stats->tx_throughput_raw);
        ewma_tp_add(&stats->rx_ewma_tp, stats->rx_throughput_raw);
@@ -3598,9 +3622,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
        stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);
        stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);
        stats->tx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->tx_throughput,
-                                                  stats->tx_cnt);
+                                                  stats->tx_cnt, interval);
        stats->rx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->rx_throughput,
-                                                  stats->rx_cnt);
+                                                  stats->rx_cnt, interval);
        stats->tx_avg_len = stats->tx_cnt ?
                            DIV_ROUND_DOWN_ULL(stats->tx_unicast, stats->tx_cnt) : 0;
        stats->rx_avg_len = stats->rx_cnt ?
@@ -3626,10 +3650,12 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
        unsigned int link_id;
        bool tfc_changed;
 
-       tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats);
+       tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats,
+                                              RTW89_TFC_INTERVAL_2SEC);
 
        rtw89_for_each_rtwvif(rtwdev, rtwvif) {
-               rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats);
+               rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats,
+                                        RTW89_TFC_INTERVAL_2SEC);
 
                rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
                        rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link);
@@ -3649,8 +3675,8 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
                if (rtwvif->offchan)
                        continue;
 
-               if (rtwvif->stats.tx_tfc_lv != RTW89_TFC_IDLE ||
-                   rtwvif->stats.rx_tfc_lv != RTW89_TFC_IDLE)
+               if (rtwvif->stats_ps.tx_tfc_lv >= RTW89_TFC_MID ||
+                   rtwvif->stats_ps.rx_tfc_lv >= RTW89_TFC_MID)
                        continue;
 
                vif = rtwvif_to_vif(rtwvif);
@@ -3789,6 +3815,34 @@ static void rtw89_core_mlo_track(struct rtw89_dev *rtwdev)
        }
 }
 
+static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+       struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+                                               track_ps_work.work);
+       struct rtw89_vif *rtwvif;
+
+       lockdep_assert_wiphy(wiphy);
+
+       if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags))
+               return;
+
+       if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+               return;
+
+       wiphy_delayed_work_queue(wiphy, &rtwdev->track_ps_work,
+                                RTW89_TRACK_PS_WORK_PERIOD);
+
+       rtw89_for_each_rtwvif(rtwdev, rtwvif)
+               rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps,
+                                        RTW89_TFC_INTERVAL_100MS);
+
+       if (rtwdev->scanning)
+               return;
+
+       if (rtwdev->lps_enabled && !rtwdev->btc.lps)
+               rtw89_enter_lps_track(rtwdev);
+}
+
 static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
 {
        struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
@@ -4875,6 +4929,8 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
 
        wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_work,
                                 RTW89_TRACK_WORK_PERIOD);
+       wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_ps_work,
+                                RTW89_TRACK_PS_WORK_PERIOD);
 
        set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
 
@@ -4909,6 +4965,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)
        wiphy_work_cancel(wiphy, &btc->icmp_notify_work);
        cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work);
        wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work);
+       wiphy_delayed_work_cancel(wiphy, &rtwdev->track_ps_work);
        wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work);
        wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work);
        wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work);
@@ -5136,6 +5193,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
        INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
        INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
        wiphy_delayed_work_init(&rtwdev->track_work, rtw89_track_work);
+       wiphy_delayed_work_init(&rtwdev->track_ps_work, rtw89_track_ps_work);
        wiphy_delayed_work_init(&rtwdev->chanctx_work, rtw89_chanctx_work);
        wiphy_delayed_work_init(&rtwdev->coex_act1_work, rtw89_coex_act1_work);
        wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);
index a08d07e44498db88095ff3861cb3528dc43aa32a..1d8f89e83c9a3d0ecf01bc3db6bf459b650f30c6 100644 (file)
@@ -40,6 +40,7 @@ extern const struct ieee80211_ops rtw89_ops;
 #define BYPASS_CR_DATA 0xbabecafe
 
 #define RTW89_TRACK_WORK_PERIOD        round_jiffies_relative(HZ * 2)
+#define RTW89_TRACK_PS_WORK_PERIOD msecs_to_jiffies(100)
 #define RTW89_FORBID_BA_TIMER round_jiffies_relative(HZ * 4)
 #define CFO_TRACK_MAX_USER 64
 #define MAX_RSSI 110
@@ -1392,6 +1393,11 @@ struct rtw89_btc_wl_smap {
        u32 emlsr: 1;
 };
 
+enum rtw89_tfc_interval {
+       RTW89_TFC_INTERVAL_100MS,
+       RTW89_TFC_INTERVAL_2SEC,
+};
+
 enum rtw89_tfc_lv {
        RTW89_TFC_IDLE,
        RTW89_TFC_ULTRA_LOW,
@@ -1400,7 +1406,6 @@ enum rtw89_tfc_lv {
        RTW89_TFC_HIGH,
 };
 
-#define RTW89_TP_SHIFT 18 /* bytes/2s --> Mbps */
 DECLARE_EWMA(tp, 10, 2);
 
 struct rtw89_traffic_stats {
@@ -5943,6 +5948,7 @@ struct rtw89_dev {
        } bbs[RTW89_PHY_NUM];
 
        struct wiphy_delayed_work track_work;
+       struct wiphy_delayed_work track_ps_work;
        struct wiphy_delayed_work chanctx_work;
        struct wiphy_delayed_work coex_act1_work;
        struct wiphy_delayed_work coex_bt_devinfo_work;
@@ -5993,6 +5999,7 @@ struct rtw89_vif {
        __be32 ip_addr;
 
        struct rtw89_traffic_stats stats;
+       struct rtw89_traffic_stats stats_ps;
        u32 tdls_peer;
 
        struct ieee80211_scan_ies *scan_ies;
@@ -7305,6 +7312,17 @@ static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev)
        return false;
 }
 
+static inline u32 rtw89_bytes_to_mbps(u64 bytes, enum rtw89_tfc_interval interval)
+{
+       switch (interval) {
+       default:
+       case RTW89_TFC_INTERVAL_2SEC:
+               return bytes >> 18; /* bytes/2s --> Mbps */;
+       case RTW89_TFC_INTERVAL_100MS:
+               return (bytes * 10) >> 17; /* bytes/100ms --> Mbps */
+       }
+}
+
 int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel);
 int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
index a3ae1e654a98d9be5c9b8d0df15110ae6d0972a4..c982ad633b4a516014850c686c167b9ee70814e7 100644 (file)
@@ -1772,6 +1772,7 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw,
 
        set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags);
        wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work);
+       wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_ps_work);
 
        ret = rtw89_wow_suspend(rtwdev, wowlan);
        if (ret) {
@@ -1797,6 +1798,8 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw)
        clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags);
        wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work,
                                 RTW89_TRACK_WORK_PERIOD);
+       wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_ps_work,
+                                RTW89_TRACK_PS_WORK_PERIOD);
 
        return ret ? 1 : 0;
 }
index d504518b8a571be02d1fd350b10b00bc00684d66..bb39fdbcba0d80e27e406e4fab3a5c53b209eb78 100644 (file)
@@ -492,6 +492,7 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
        case SER_EV_STATE_IN:
                wiphy_lock(wiphy);
                wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work);
+               wiphy_delayed_work_cancel(wiphy, &rtwdev->track_ps_work);
                wiphy_unlock(wiphy);
                drv_stop_tx(ser);
 
@@ -525,6 +526,8 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
                drv_resume_tx(ser);
                wiphy_delayed_work_queue(wiphy, &rtwdev->track_work,
                                         RTW89_TRACK_WORK_PERIOD);
+               wiphy_delayed_work_queue(wiphy, &rtwdev->track_ps_work,
+                                        RTW89_TRACK_PS_WORK_PERIOD);
                break;
 
        default:
index 34a0ab49bd7a9e064701d9a5da135dc4a3c1f9c1..c935d6683d833c567de67075798e296425f25e4a 100644 (file)
@@ -12,7 +12,7 @@
 #include "util.h"
 #include "wow.h"
 
-void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)
+void __rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)
 {
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
        struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
index f91991e8f2e30e60f4b5662619ff097f207c26f1..6606528d31c7de58c7c8af6fc0d0cc6d3df1e17e 100644 (file)
@@ -116,9 +116,21 @@ static inline bool rtw_wow_has_mgd_features(struct rtw89_dev *rtwdev)
        return !bitmap_empty(rtw_wow->flags, RTW89_WOW_FLAG_NUM);
 }
 
+void __rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb);
+
+static inline
+void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       if (likely(!ieee80211_is_assoc_req(hdr->frame_control)))
+               return;
+
+       __rtw89_wow_parse_akm(rtwdev, skb);
+}
+
 int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan);
 int rtw89_wow_resume(struct rtw89_dev *rtwdev);
-void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb);
 #else
 static inline
 void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)