Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: add some support for RX OMI power saving

In order to save power, it can be desirable to change the
RX operating mode using OMI to reduce the bandwidth. As the
handshake must be done in the HTC+ field, it cannot be done
by mac80211 directly, so expose functions to the driver to
request and finalize the necessary updates.

Note that RX OMI really only changes what the peer (AP) will
transmit to us, but in order to use it to actually save some
power (by reducing the listen bandwidth) we also update rate
scaling and then the channel context's mindef accordingly.

The updates are split into two in order to sequence them
correctly, when reducing bandwidth first reduce the rate
scaling and thus TX, then send OMI, then reduce the listen
bandwidth (chandef); when increasing bandwidth this is the
other way around. This also requires tracking in different
variables which part is applicable already.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250101070249.2c1a1934bd73.I4e90fd503504e37f9eac5bdae62e3f07e7071275@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

+323 -5
+46
include/net/mac80211.h
··· 2340 2340 IEEE80211_STA_RX_BW_320, 2341 2341 }; 2342 2342 2343 + #define IEEE80211_STA_RX_BW_MAX IEEE80211_STA_RX_BW_320 2344 + 2343 2345 /** 2344 2346 * struct ieee80211_sta_rates - station rate selection table 2345 2347 * ··· 7739 7737 return IEEE80211_STA_RX_BW_320; 7740 7738 } 7741 7739 } 7740 + 7741 + /** 7742 + * ieee80211_prepare_rx_omi_bw - prepare for sending BW RX OMI 7743 + * @link_sta: the link STA the OMI is going to be sent to 7744 + * @bw: the bandwidth requested 7745 + * 7746 + * When the driver decides to do RX OMI to change bandwidth with a STA 7747 + * it calls this function to prepare, then sends the OMI, and finally 7748 + * calls ieee80211_finalize_rx_omi_bw(). 7749 + * 7750 + * Note that the (link) STA rate control is updated accordingly as well, 7751 + * but the chanctx might not be updated if there are other users. 7752 + * If the intention is to reduce the listen bandwidth, the driver must 7753 + * ensure there are no TDLS stations nor other uses of the chanctx. 7754 + * 7755 + * Also note that in order to sequence correctly, narrowing bandwidth 7756 + * will only happen in ieee80211_finalize_rx_omi_bw(), whereas widening 7757 + * again (e.g. going back to normal) will happen here. 7758 + * 7759 + * Note that we treat this symmetrically, so if the driver calls this 7760 + * and tells the peer to only send with a lower bandwidth, we assume 7761 + * that the driver also wants to only send at that lower bandwidth, to 7762 + * allow narrowing of the chanctx request for this station/interface. 7763 + * 7764 + * Finally, the driver must ensure that if the function returned %true, 7765 + * ieee80211_finalize_rx_omi_bw() is also called, even for example in 7766 + * case of HW restart. 7767 + * 7768 + * Context: Must be called with wiphy mutex held, and will call back 7769 + * into the driver, so ensure no driver locks are held. 7770 + * 7771 + * Return: %true if changes are going to be made, %false otherwise 7772 + */ 7773 + bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *link_sta, 7774 + enum ieee80211_sta_rx_bandwidth bw); 7775 + 7776 + /** 7777 + * ieee80211_finalize_rx_omi_bw - finalize BW RX OMI update 7778 + * @link_sta: the link STA the OMI was sent to 7779 + * 7780 + * See ieee80211_client_prepare_rx_omi_bw(). Context is the same here 7781 + * as well. 7782 + */ 7783 + void ieee80211_finalize_rx_omi_bw(struct ieee80211_link_sta *link_sta); 7742 7784 7743 7785 /* for older drivers - let's not document these ... */ 7744 7786 int ieee80211_emulate_add_chanctx(struct ieee80211_hw *hw,
+7
net/mac80211/chan.c
··· 247 247 if (!link_sta) 248 248 return NL80211_CHAN_WIDTH_20_NOHT; 249 249 250 + /* 251 + * We assume that TX/RX might be asymmetric (so e.g. VHT operating 252 + * mode notification changes what a STA wants to receive, but not 253 + * necessarily what it will transmit to us), and therefore use the 254 + * capabilities here. Calling it RX bandwidth capability is a bit 255 + * wrong though, since capabilities are in fact symmetric. 256 + */ 250 257 width = ieee80211_sta_cap_rx_bw(link_sta); 251 258 252 259 switch (width) {
+118 -1
net/mac80211/he.c
··· 3 3 * HE handling 4 4 * 5 5 * Copyright(c) 2017 Intel Deutschland GmbH 6 - * Copyright(c) 2019 - 2023 Intel Corporation 6 + * Copyright(c) 2019 - 2024 Intel Corporation 7 7 */ 8 8 9 9 #include "ieee80211_i.h" 10 + #include "rate.h" 10 11 11 12 static void 12 13 ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_capa, ··· 249 248 he_obss_pd->enable = true; 250 249 } 251 250 } 251 + 252 + static void ieee80211_link_sta_rc_update_omi(struct ieee80211_link_data *link, 253 + struct link_sta_info *link_sta) 254 + { 255 + struct ieee80211_sub_if_data *sdata = link->sdata; 256 + struct ieee80211_supported_band *sband; 257 + enum ieee80211_sta_rx_bandwidth new_bw; 258 + enum nl80211_band band; 259 + 260 + band = link->conf->chanreq.oper.chan->band; 261 + sband = sdata->local->hw.wiphy->bands[band]; 262 + 263 + new_bw = ieee80211_sta_cur_vht_bw(link_sta); 264 + if (link_sta->pub->bandwidth == new_bw) 265 + return; 266 + 267 + link_sta->pub->bandwidth = new_bw; 268 + rate_control_rate_update(sdata->local, sband, link_sta, 269 + IEEE80211_RC_BW_CHANGED); 270 + } 271 + 272 + bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta, 273 + enum ieee80211_sta_rx_bandwidth bw) 274 + { 275 + struct sta_info *sta = container_of(pub_link_sta->sta, 276 + struct sta_info, sta); 277 + struct ieee80211_local *local = sta->sdata->local; 278 + struct link_sta_info *link_sta = 279 + sdata_dereference(sta->link[pub_link_sta->link_id], sta->sdata); 280 + struct ieee80211_link_data *link = 281 + sdata_dereference(sta->sdata->link[pub_link_sta->link_id], 282 + sta->sdata); 283 + struct ieee80211_chanctx_conf *conf; 284 + struct ieee80211_chanctx *chanctx; 285 + bool ret; 286 + 287 + if (WARN_ON(!link || !link_sta || link_sta->pub != pub_link_sta)) 288 + return false; 289 + 290 + conf = sdata_dereference(link->conf->chanctx_conf, sta->sdata); 291 + if (WARN_ON(!conf)) 292 + return false; 293 + 294 + trace_api_prepare_rx_omi_bw(local, sta->sdata, link_sta, bw); 295 + 296 + chanctx = container_of(conf, typeof(*chanctx), conf); 297 + 298 + if (link_sta->rx_omi_bw_staging == bw) { 299 + ret = false; 300 + goto trace; 301 + } 302 + 303 + /* must call this API in pairs */ 304 + if (WARN_ON(link_sta->rx_omi_bw_tx != link_sta->rx_omi_bw_staging || 305 + link_sta->rx_omi_bw_rx != link_sta->rx_omi_bw_staging)) { 306 + ret = false; 307 + goto trace; 308 + } 309 + 310 + if (bw < link_sta->rx_omi_bw_staging) { 311 + link_sta->rx_omi_bw_tx = bw; 312 + ieee80211_link_sta_rc_update_omi(link, link_sta); 313 + } else { 314 + link_sta->rx_omi_bw_rx = bw; 315 + ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false); 316 + } 317 + 318 + link_sta->rx_omi_bw_staging = bw; 319 + ret = true; 320 + trace: 321 + trace_api_return_bool(local, ret); 322 + return ret; 323 + } 324 + EXPORT_SYMBOL_GPL(ieee80211_prepare_rx_omi_bw); 325 + 326 + void ieee80211_finalize_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta) 327 + { 328 + struct sta_info *sta = container_of(pub_link_sta->sta, 329 + struct sta_info, sta); 330 + struct ieee80211_local *local = sta->sdata->local; 331 + struct link_sta_info *link_sta = 332 + sdata_dereference(sta->link[pub_link_sta->link_id], sta->sdata); 333 + struct ieee80211_link_data *link = 334 + sdata_dereference(sta->sdata->link[pub_link_sta->link_id], 335 + sta->sdata); 336 + struct ieee80211_chanctx_conf *conf; 337 + struct ieee80211_chanctx *chanctx; 338 + 339 + if (WARN_ON(!link || !link_sta || link_sta->pub != pub_link_sta)) 340 + return; 341 + 342 + conf = sdata_dereference(link->conf->chanctx_conf, sta->sdata); 343 + if (WARN_ON(!conf)) 344 + return; 345 + 346 + trace_api_finalize_rx_omi_bw(local, sta->sdata, link_sta); 347 + 348 + chanctx = container_of(conf, typeof(*chanctx), conf); 349 + 350 + if (link_sta->rx_omi_bw_tx != link_sta->rx_omi_bw_staging) { 351 + /* rate control in finalize only when widening bandwidth */ 352 + WARN_ON(link_sta->rx_omi_bw_tx > link_sta->rx_omi_bw_staging); 353 + link_sta->rx_omi_bw_tx = link_sta->rx_omi_bw_staging; 354 + ieee80211_link_sta_rc_update_omi(link, link_sta); 355 + } 356 + 357 + if (link_sta->rx_omi_bw_rx != link_sta->rx_omi_bw_staging) { 358 + /* channel context in finalize only when narrowing bandwidth */ 359 + WARN_ON(link_sta->rx_omi_bw_rx < link_sta->rx_omi_bw_staging); 360 + link_sta->rx_omi_bw_rx = link_sta->rx_omi_bw_staging; 361 + ieee80211_recalc_chanctx_min_def(local, chanctx, NULL, false); 362 + } 363 + 364 + trace_api_return_void(local); 365 + } 366 + EXPORT_SYMBOL_GPL(ieee80211_finalize_rx_omi_bw);
+18
net/mac80211/sta_info.c
··· 509 509 for (i = 0; i < ARRAY_SIZE(link_info->rx_stats_avg.chain_signal); i++) 510 510 ewma_signal_init(&link_info->rx_stats_avg.chain_signal[i]); 511 511 512 + link_info->rx_omi_bw_rx = IEEE80211_STA_RX_BW_MAX; 513 + link_info->rx_omi_bw_tx = IEEE80211_STA_RX_BW_MAX; 514 + link_info->rx_omi_bw_staging = IEEE80211_STA_RX_BW_MAX; 515 + 516 + /* 517 + * Cause (a) warning(s) if IEEE80211_STA_RX_BW_MAX != 320 518 + * or if new values are added to the enum. 519 + */ 520 + switch (link_info->cur_max_bandwidth) { 521 + case IEEE80211_STA_RX_BW_20: 522 + case IEEE80211_STA_RX_BW_40: 523 + case IEEE80211_STA_RX_BW_80: 524 + case IEEE80211_STA_RX_BW_160: 525 + case IEEE80211_STA_RX_BW_MAX: 526 + /* intentionally nothing */ 527 + break; 528 + } 529 + 512 530 return 0; 513 531 } 514 532
+7
net/mac80211/sta_info.h
··· 512 512 * @status_stats.avg_ack_signal: average ACK signal 513 513 * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, 514 514 * taken from HT/VHT capabilities or VHT operating mode notification 515 + * @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX 516 + * @rx_omi_bw_tx: RX OMI bandwidth restriction to apply for TX 517 + * @rx_omi_bw_staging: RX OMI bandwidth restriction to apply later 518 + * during finalize 515 519 * @debugfs_dir: debug filesystem directory dentry 516 520 * @pub: public (driver visible) link STA data 517 521 * TODO Move other link params from sta_info as required for MLD operation ··· 565 561 } tx_stats; 566 562 567 563 enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; 564 + enum ieee80211_sta_rx_bandwidth rx_omi_bw_rx, 565 + rx_omi_bw_tx, 566 + rx_omi_bw_staging; 568 567 569 568 #ifdef CONFIG_MAC80211_DEBUGFS 570 569 struct dentry *debugfs_dir;
+98
net/mac80211/trace.h
··· 2590 2590 * Tracing for API calls that drivers call. 2591 2591 */ 2592 2592 2593 + TRACE_EVENT(api_return_bool, 2594 + TP_PROTO(struct ieee80211_local *local, bool result), 2595 + 2596 + TP_ARGS(local, result), 2597 + 2598 + TP_STRUCT__entry( 2599 + LOCAL_ENTRY 2600 + __field(bool, result) 2601 + ), 2602 + 2603 + TP_fast_assign( 2604 + LOCAL_ASSIGN; 2605 + __entry->result = result; 2606 + ), 2607 + 2608 + TP_printk( 2609 + LOCAL_PR_FMT " result=%d", 2610 + LOCAL_PR_ARG, __entry->result 2611 + ) 2612 + ); 2613 + 2614 + TRACE_EVENT(api_return_void, 2615 + TP_PROTO(struct ieee80211_local *local), 2616 + 2617 + TP_ARGS(local), 2618 + 2619 + TP_STRUCT__entry( 2620 + LOCAL_ENTRY 2621 + ), 2622 + 2623 + TP_fast_assign( 2624 + LOCAL_ASSIGN; 2625 + ), 2626 + 2627 + TP_printk( 2628 + LOCAL_PR_FMT, LOCAL_PR_ARG 2629 + ) 2630 + ); 2631 + 2593 2632 TRACE_EVENT(api_start_tx_ba_session, 2594 2633 TP_PROTO(struct ieee80211_sta *sta, u16 tid), 2595 2634 ··· 3090 3051 TP_printk( 3091 3052 LOCAL_PR_FMT " " VIF_PR_FMT " link:%d, smps_mode:%d", 3092 3053 LOCAL_PR_ARG, VIF_PR_ARG, __entry->link_id, __entry->smps_mode 3054 + ) 3055 + ); 3056 + 3057 + TRACE_EVENT(api_prepare_rx_omi_bw, 3058 + TP_PROTO(struct ieee80211_local *local, 3059 + struct ieee80211_sub_if_data *sdata, 3060 + struct link_sta_info *link_sta, 3061 + enum ieee80211_sta_rx_bandwidth bw), 3062 + 3063 + TP_ARGS(local, sdata, link_sta, bw), 3064 + 3065 + TP_STRUCT__entry( 3066 + LOCAL_ENTRY 3067 + VIF_ENTRY 3068 + STA_ENTRY 3069 + __field(int, link_id) 3070 + __field(u32, bw) 3071 + __field(bool, result) 3072 + ), 3073 + 3074 + TP_fast_assign( 3075 + LOCAL_ASSIGN; 3076 + VIF_ASSIGN; 3077 + STA_NAMED_ASSIGN(link_sta->sta); 3078 + __entry->link_id = link_sta->link_id; 3079 + __entry->bw = bw; 3080 + ), 3081 + 3082 + TP_printk( 3083 + LOCAL_PR_FMT " " VIF_PR_FMT " " STA_PR_FMT " link:%d, bw:%d", 3084 + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, 3085 + __entry->link_id, __entry->bw 3086 + ) 3087 + ); 3088 + 3089 + TRACE_EVENT(api_finalize_rx_omi_bw, 3090 + TP_PROTO(struct ieee80211_local *local, 3091 + struct ieee80211_sub_if_data *sdata, 3092 + struct link_sta_info *link_sta), 3093 + 3094 + TP_ARGS(local, sdata, link_sta), 3095 + 3096 + TP_STRUCT__entry( 3097 + LOCAL_ENTRY 3098 + VIF_ENTRY 3099 + STA_ENTRY 3100 + __field(int, link_id) 3101 + ), 3102 + 3103 + TP_fast_assign( 3104 + LOCAL_ASSIGN; 3105 + VIF_ASSIGN; 3106 + STA_NAMED_ASSIGN(link_sta->sta); 3107 + __entry->link_id = link_sta->link_id; 3108 + ), 3109 + 3110 + TP_printk( 3111 + LOCAL_PR_FMT " " VIF_PR_FMT " " STA_PR_FMT " link:%d", 3112 + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->link_id 3093 3113 ) 3094 3114 ); 3095 3115
+29 -4
net/mac80211/vht.c
··· 350 350 } 351 351 352 352 /* FIXME: move this to some better location - parses HE/EHT now */ 353 - enum ieee80211_sta_rx_bandwidth 354 - _ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, 355 - struct cfg80211_chan_def *chandef) 353 + static enum ieee80211_sta_rx_bandwidth 354 + __ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, 355 + struct cfg80211_chan_def *chandef) 356 356 { 357 357 unsigned int link_id = link_sta->link_id; 358 358 struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata; ··· 421 421 return IEEE80211_STA_RX_BW_160; 422 422 423 423 return IEEE80211_STA_RX_BW_80; 424 + } 425 + 426 + enum ieee80211_sta_rx_bandwidth 427 + _ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, 428 + struct cfg80211_chan_def *chandef) 429 + { 430 + /* 431 + * With RX OMI, also pretend that the STA's capability changed. 432 + * Of course this isn't really true, it didn't change, only our 433 + * RX capability was changed by notifying RX OMI to the STA. 434 + * The purpose, however, is to save power, and that requires 435 + * changing also transmissions to the AP and the chanctx. The 436 + * transmissions depend on link_sta->bandwidth which is set in 437 + * _ieee80211_sta_cur_vht_bw() below, but the chanctx depends 438 + * on the result of this function which is also called by 439 + * _ieee80211_sta_cur_vht_bw(), so we need to do that here as 440 + * well. This is sufficient for the steady state, but during 441 + * the transition we already need to change TX/RX separately, 442 + * so _ieee80211_sta_cur_vht_bw() below applies the _tx one. 443 + */ 444 + return min(__ieee80211_sta_cap_rx_bw(link_sta, chandef), 445 + link_sta->rx_omi_bw_rx); 424 446 } 425 447 426 448 enum nl80211_chan_width ··· 525 503 rcu_read_unlock(); 526 504 } 527 505 528 - bw = _ieee80211_sta_cap_rx_bw(link_sta, chandef); 506 + /* intentionally do not take rx_bw_omi_rx into account */ 507 + bw = __ieee80211_sta_cap_rx_bw(link_sta, chandef); 529 508 bw = min(bw, link_sta->cur_max_bandwidth); 509 + /* but do apply rx_omi_bw_tx */ 510 + bw = min(bw, link_sta->rx_omi_bw_tx); 530 511 531 512 /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of 532 513 * IEEE80211-2016 specification makes higher bandwidth operation