wifi: rtw89: enter power save mode aggressively

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
This commit is contained in:
Chin-Yen Lee
2025-07-01 15:38:39 +08:00
committed by Ping-Ke Shih
parent 0030088148
commit 480dd4dddf
6 changed files with 123 additions and 29 deletions

View 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);

View 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,

View 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;
}

View 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:

View 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;

View 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)