From: Greg Kroah-Hartman Date: Mon, 6 Oct 2025 10:24:28 +0000 (+0200) Subject: 6.16-stable patches X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9e3bef05ec4138f072515b8aa57d9587b1c0e822;p=thirdparty%2Fkernel%2Fstable-queue.git 6.16-stable patches added patches: wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch wifi-rtw89-mcc-stop-tx-during-mcc-prepare.patch --- diff --git a/queue-6.16/series b/queue-6.16/series index 7e7bd8e961..060ade6938 100644 --- a/queue-6.16/series +++ b/queue-6.16/series @@ -1,2 +1,4 @@ alsa-hda-tas2781-fix-the-order-of-tas2781-calibrated-data.patch drm-amdgpu-enable-mes-lr_compute_wa-by-default.patch +wifi-rtw89-mcc-stop-tx-during-mcc-prepare.patch +wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch diff --git a/queue-6.16/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch b/queue-6.16/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch new file mode 100644 index 0000000000..e29a9af1be --- /dev/null +++ b/queue-6.16/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch @@ -0,0 +1,326 @@ +From stable+bounces-183238-greg=kroah.com@vger.kernel.org Fri Oct 3 17:20:17 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 11:20:05 -0400 +Subject: wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait() +To: stable@vger.kernel.org +Cc: Fedor Pchelkin , Zong-Zhe Yang , Ping-Ke Shih , Sasha Levin +Message-ID: <20251003152005.3176758-2-sashal@kernel.org> + +From: Fedor Pchelkin + +[ Upstream commit 3e31a6bc07312b448fad3b45de578471f86f0e77 ] + +There is a bug observed when rtw89_core_tx_kick_off_and_wait() tries to +access already freed skb_data: + + BUG: KFENCE: use-after-free write in rtw89_core_tx_kick_off_and_wait drivers/net/wireless/realtek/rtw89/core.c:1110 + + CPU: 6 UID: 0 PID: 41377 Comm: kworker/u64:24 Not tainted 6.17.0-rc1+ #1 PREEMPT(lazy) + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS edk2-20250523-14.fc42 05/23/2025 + Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] + + Use-after-free write at 0x0000000020309d9d (in kfence-#251): + rtw89_core_tx_kick_off_and_wait drivers/net/wireless/realtek/rtw89/core.c:1110 + rtw89_core_scan_complete drivers/net/wireless/realtek/rtw89/core.c:5338 + rtw89_hw_scan_complete_cb drivers/net/wireless/realtek/rtw89/fw.c:7979 + rtw89_chanctx_proceed_cb drivers/net/wireless/realtek/rtw89/chan.c:3165 + rtw89_chanctx_proceed drivers/net/wireless/realtek/rtw89/chan.h:141 + rtw89_hw_scan_complete drivers/net/wireless/realtek/rtw89/fw.c:8012 + rtw89_mac_c2h_scanofld_rsp drivers/net/wireless/realtek/rtw89/mac.c:5059 + rtw89_fw_c2h_work drivers/net/wireless/realtek/rtw89/fw.c:6758 + process_one_work kernel/workqueue.c:3241 + worker_thread kernel/workqueue.c:3400 + kthread kernel/kthread.c:463 + ret_from_fork arch/x86/kernel/process.c:154 + ret_from_fork_asm arch/x86/entry/entry_64.S:258 + + kfence-#251: 0x0000000056e2393d-0x000000009943cb62, size=232, cache=skbuff_head_cache + + allocated by task 41377 on cpu 6 at 77869.159548s (0.009551s ago): + __alloc_skb net/core/skbuff.c:659 + __netdev_alloc_skb net/core/skbuff.c:734 + ieee80211_nullfunc_get net/mac80211/tx.c:5844 + rtw89_core_send_nullfunc drivers/net/wireless/realtek/rtw89/core.c:3431 + rtw89_core_scan_complete drivers/net/wireless/realtek/rtw89/core.c:5338 + rtw89_hw_scan_complete_cb drivers/net/wireless/realtek/rtw89/fw.c:7979 + rtw89_chanctx_proceed_cb drivers/net/wireless/realtek/rtw89/chan.c:3165 + rtw89_chanctx_proceed drivers/net/wireless/realtek/rtw89/chan.c:3194 + rtw89_hw_scan_complete drivers/net/wireless/realtek/rtw89/fw.c:8012 + rtw89_mac_c2h_scanofld_rsp drivers/net/wireless/realtek/rtw89/mac.c:5059 + rtw89_fw_c2h_work drivers/net/wireless/realtek/rtw89/fw.c:6758 + process_one_work kernel/workqueue.c:3241 + worker_thread kernel/workqueue.c:3400 + kthread kernel/kthread.c:463 + ret_from_fork arch/x86/kernel/process.c:154 + ret_from_fork_asm arch/x86/entry/entry_64.S:258 + + freed by task 1045 on cpu 9 at 77869.168393s (0.001557s ago): + ieee80211_tx_status_skb net/mac80211/status.c:1117 + rtw89_pci_release_txwd_skb drivers/net/wireless/realtek/rtw89/pci.c:564 + rtw89_pci_release_tx_skbs.isra.0 drivers/net/wireless/realtek/rtw89/pci.c:651 + rtw89_pci_release_tx drivers/net/wireless/realtek/rtw89/pci.c:676 + rtw89_pci_napi_poll drivers/net/wireless/realtek/rtw89/pci.c:4238 + __napi_poll net/core/dev.c:7495 + net_rx_action net/core/dev.c:7557 net/core/dev.c:7684 + handle_softirqs kernel/softirq.c:580 + do_softirq.part.0 kernel/softirq.c:480 + __local_bh_enable_ip kernel/softirq.c:407 + rtw89_pci_interrupt_threadfn drivers/net/wireless/realtek/rtw89/pci.c:927 + irq_thread_fn kernel/irq/manage.c:1133 + irq_thread kernel/irq/manage.c:1257 + kthread kernel/kthread.c:463 + ret_from_fork arch/x86/kernel/process.c:154 + ret_from_fork_asm arch/x86/entry/entry_64.S:258 + +It is a consequence of a race between the waiting and the signaling side +of the completion: + + Waiting thread Completing thread + +rtw89_core_tx_kick_off_and_wait() + rcu_assign_pointer(skb_data->wait, wait) + /* start waiting */ + wait_for_completion_timeout() + rtw89_pci_tx_status() + rtw89_core_tx_wait_complete() + rcu_read_lock() + /* signals completion and + * proceeds further + */ + complete(&wait->completion) + rcu_read_unlock() + ... + /* frees skb_data */ + ieee80211_tx_status_ni() + /* returns (exit status doesn't matter) */ + wait_for_completion_timeout() + ... + /* accesses the already freed skb_data */ + rcu_assign_pointer(skb_data->wait, NULL) + +The completing side might proceed and free the underlying skb even before +the waiting side is fully awoken and run to execution. Actually the race +happens regardless of wait_for_completion_timeout() exit status, e.g. +the waiting side may hit a timeout and the concurrent completing side is +still able to free the skb. + +Skbs which are sent by rtw89_core_tx_kick_off_and_wait() are owned by the +driver. They don't come from core ieee80211 stack so no need to pass them +to ieee80211_tx_status_ni() on completing side. + +Introduce a work function which will act as a garbage collector for +rtw89_tx_wait_info objects and the associated skbs. Thus no potentially +heavy locks are required on the completing side. + +Found by Linux Verification Center (linuxtesting.org). + +Fixes: 1ae5ca615285 ("wifi: rtw89: add function to wait for completion of TX skbs") +Cc: stable@vger.kernel.org +Suggested-by: Zong-Zhe Yang +Signed-off-by: Fedor Pchelkin +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20250919210852.823912-2-pchelkin@ispras.ru +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtw89/core.c | 30 ++++++++++++++++++++----- + drivers/net/wireless/realtek/rtw89/core.h | 35 ++++++++++++++++++++++++++++-- + drivers/net/wireless/realtek/rtw89/pci.c | 3 +- + drivers/net/wireless/realtek/rtw89/ser.c | 2 + + 4 files changed, 61 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/realtek/rtw89/core.c ++++ b/drivers/net/wireless/realtek/rtw89/core.c +@@ -1050,6 +1050,14 @@ rtw89_core_tx_update_desc_info(struct rt + } + } + ++static void rtw89_tx_wait_work(struct wiphy *wiphy, struct wiphy_work *work) ++{ ++ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ++ tx_wait_work.work); ++ ++ rtw89_tx_wait_list_clear(rtwdev); ++} ++ + void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel) + { + u8 ch_dma; +@@ -1067,6 +1075,8 @@ int rtw89_core_tx_kick_off_and_wait(stru + unsigned long time_left; + int ret = 0; + ++ lockdep_assert_wiphy(rtwdev->hw->wiphy); ++ + wait = kzalloc(sizeof(*wait), GFP_KERNEL); + if (!wait) { + rtw89_core_tx_kick_off(rtwdev, qsel); +@@ -1074,18 +1084,23 @@ int rtw89_core_tx_kick_off_and_wait(stru + } + + init_completion(&wait->completion); ++ wait->skb = skb; + rcu_assign_pointer(skb_data->wait, wait); + + rtw89_core_tx_kick_off(rtwdev, qsel); + time_left = wait_for_completion_timeout(&wait->completion, + msecs_to_jiffies(timeout)); +- if (time_left == 0) +- ret = -ETIMEDOUT; +- else if (!wait->tx_done) +- ret = -EAGAIN; + +- rcu_assign_pointer(skb_data->wait, NULL); +- kfree_rcu(wait, rcu_head); ++ if (time_left == 0) { ++ ret = -ETIMEDOUT; ++ list_add_tail(&wait->list, &rtwdev->tx_waits); ++ wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->tx_wait_work, ++ RTW89_TX_WAIT_WORK_TIMEOUT); ++ } else { ++ if (!wait->tx_done) ++ ret = -EAGAIN; ++ rtw89_tx_wait_release(wait); ++ } + + return ret; + } +@@ -4810,6 +4825,7 @@ void rtw89_core_stop(struct rtw89_dev *r + wiphy_work_cancel(wiphy, &btc->dhcp_notify_work); + wiphy_work_cancel(wiphy, &btc->icmp_notify_work); + cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work); ++ wiphy_delayed_work_cancel(wiphy, &rtwdev->tx_wait_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work); +@@ -5034,6 +5050,7 @@ int rtw89_core_init(struct rtw89_dev *rt + INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]); + } + INIT_LIST_HEAD(&rtwdev->scan_info.chan_list); ++ INIT_LIST_HEAD(&rtwdev->tx_waits); + INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work); + INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work); + INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); +@@ -5044,6 +5061,7 @@ int rtw89_core_init(struct rtw89_dev *rt + wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); + wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); + wiphy_delayed_work_init(&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work); ++ wiphy_delayed_work_init(&rtwdev->tx_wait_work, rtw89_tx_wait_work); + INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); + wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); + rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); +--- a/drivers/net/wireless/realtek/rtw89/core.h ++++ b/drivers/net/wireless/realtek/rtw89/core.h +@@ -3429,9 +3429,12 @@ struct rtw89_phy_rate_pattern { + bool enable; + }; + ++#define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500) + struct rtw89_tx_wait_info { + struct rcu_head rcu_head; ++ struct list_head list; + struct completion completion; ++ struct sk_buff *skb; + bool tx_done; + }; + +@@ -5802,6 +5805,9 @@ struct rtw89_dev { + /* used to protect rpwm */ + spinlock_t rpwm_lock; + ++ struct list_head tx_waits; ++ struct wiphy_delayed_work tx_wait_work; ++ + struct rtw89_cam_info cam_info; + + struct sk_buff_head c2h_queue; +@@ -6056,6 +6062,26 @@ rtw89_assoc_link_rcu_dereference(struct + list_first_entry_or_null(&p->dlink_pool, typeof(*p->links_inst), dlink_schd); \ + }) + ++static inline void rtw89_tx_wait_release(struct rtw89_tx_wait_info *wait) ++{ ++ dev_kfree_skb_any(wait->skb); ++ kfree_rcu(wait, rcu_head); ++} ++ ++static inline void rtw89_tx_wait_list_clear(struct rtw89_dev *rtwdev) ++{ ++ struct rtw89_tx_wait_info *wait, *tmp; ++ ++ lockdep_assert_wiphy(rtwdev->hw->wiphy); ++ ++ list_for_each_entry_safe(wait, tmp, &rtwdev->tx_waits, list) { ++ if (!completion_done(&wait->completion)) ++ continue; ++ list_del(&wait->list); ++ rtw89_tx_wait_release(wait); ++ } ++} ++ + static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req) + { +@@ -6065,6 +6091,7 @@ static inline int rtw89_hci_tx_write(str + static inline void rtw89_hci_reset(struct rtw89_dev *rtwdev) + { + rtwdev->hci.ops->reset(rtwdev); ++ rtw89_tx_wait_list_clear(rtwdev); + } + + static inline int rtw89_hci_start(struct rtw89_dev *rtwdev) +@@ -7122,11 +7149,12 @@ static inline struct sk_buff *rtw89_allo + return dev_alloc_skb(length); + } + +-static inline void rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev, ++static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev, + struct rtw89_tx_skb_data *skb_data, + bool tx_done) + { + struct rtw89_tx_wait_info *wait; ++ bool ret = false; + + rcu_read_lock(); + +@@ -7134,11 +7162,14 @@ static inline void rtw89_core_tx_wait_co + if (!wait) + goto out; + ++ ret = true; + wait->tx_done = tx_done; +- complete(&wait->completion); ++ /* Don't access skb anymore after completion */ ++ complete_all(&wait->completion); + + out: + rcu_read_unlock(); ++ return ret; + } + + static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev) +--- a/drivers/net/wireless/realtek/rtw89/pci.c ++++ b/drivers/net/wireless/realtek/rtw89/pci.c +@@ -464,7 +464,8 @@ static void rtw89_pci_tx_status(struct r + struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); + struct ieee80211_tx_info *info; + +- rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE); ++ if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE)) ++ return; + + info = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(info); +--- a/drivers/net/wireless/realtek/rtw89/ser.c ++++ b/drivers/net/wireless/realtek/rtw89/ser.c +@@ -501,7 +501,9 @@ static void ser_reset_trx_st_hdl(struct + } + + drv_stop_rx(ser); ++ wiphy_lock(wiphy); + drv_trx_reset(ser); ++ wiphy_unlock(wiphy); + + /* wait m3 */ + hal_send_m2_event(ser); diff --git a/queue-6.16/wifi-rtw89-mcc-stop-tx-during-mcc-prepare.patch b/queue-6.16/wifi-rtw89-mcc-stop-tx-during-mcc-prepare.patch new file mode 100644 index 0000000000..10c75693a5 --- /dev/null +++ b/queue-6.16/wifi-rtw89-mcc-stop-tx-during-mcc-prepare.patch @@ -0,0 +1,141 @@ +From stable+bounces-183237-greg=kroah.com@vger.kernel.org Fri Oct 3 17:20:16 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 11:20:04 -0400 +Subject: wifi: rtw89: mcc: stop TX during MCC prepare +To: stable@vger.kernel.org +Cc: Chih-Kang Chang , Ping-Ke Shih , Sasha Levin +Message-ID: <20251003152005.3176758-1-sashal@kernel.org> + +From: Chih-Kang Chang + +[ Upstream commit 182c7ff8b87e4edbb2227ede39ae0952da7a0f4a ] + +Stop TX during the MCC configuration period to prevent packet leakage. +The stop time is defined as 'start_tsf - tsf', which means the duration +from when MCC configuration begins until MCC starts. + +Signed-off-by: Chih-Kang Chang +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20250610130034.14692-6-pkshih@realtek.com +Stable-dep-of: 3e31a6bc0731 ("wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtw89/chan.c | 35 ++++++++++++++++++++++++++++++ + drivers/net/wireless/realtek/rtw89/chan.h | 2 + + drivers/net/wireless/realtek/rtw89/core.c | 2 + + drivers/net/wireless/realtek/rtw89/core.h | 2 + + 4 files changed, 41 insertions(+) + +--- a/drivers/net/wireless/realtek/rtw89/chan.c ++++ b/drivers/net/wireless/realtek/rtw89/chan.c +@@ -1595,6 +1595,35 @@ static bool rtw89_mcc_duration_decision_ + return false; + } + ++void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work) ++{ ++ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ++ mcc_prepare_done_work.work); ++ ++ lockdep_assert_wiphy(wiphy); ++ ++ ieee80211_wake_queues(rtwdev->hw); ++} ++ ++static void rtw89_mcc_prepare(struct rtw89_dev *rtwdev, bool start) ++{ ++ struct rtw89_mcc_info *mcc = &rtwdev->mcc; ++ struct rtw89_mcc_config *config = &mcc->config; ++ ++ if (start) { ++ ieee80211_stop_queues(rtwdev->hw); ++ ++ wiphy_delayed_work_queue(rtwdev->hw->wiphy, ++ &rtwdev->mcc_prepare_done_work, ++ usecs_to_jiffies(config->prepare_delay)); ++ } else { ++ wiphy_delayed_work_queue(rtwdev->hw->wiphy, ++ &rtwdev->mcc_prepare_done_work, 0); ++ wiphy_delayed_work_flush(rtwdev->hw->wiphy, ++ &rtwdev->mcc_prepare_done_work); ++ } ++} ++ + static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev) + { + struct rtw89_mcc_info *mcc = &rtwdev->mcc; +@@ -1630,6 +1659,8 @@ static int rtw89_mcc_fill_start_tsf(stru + + config->start_tsf = start_tsf; + config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf; ++ config->prepare_delay = start_tsf - tsf; ++ + return 0; + } + +@@ -2219,6 +2250,8 @@ static int rtw89_mcc_start(struct rtw89_ + rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_START); + + rtw89_mcc_start_beacon_noa(rtwdev); ++ ++ rtw89_mcc_prepare(rtwdev, true); + return 0; + } + +@@ -2307,6 +2340,8 @@ static void rtw89_mcc_stop(struct rtw89_ + rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP); + + rtw89_mcc_stop_beacon_noa(rtwdev); ++ ++ rtw89_mcc_prepare(rtwdev, false); + } + + static int rtw89_mcc_update(struct rtw89_dev *rtwdev) +--- a/drivers/net/wireless/realtek/rtw89/chan.h ++++ b/drivers/net/wireless/realtek/rtw89/chan.h +@@ -129,6 +129,8 @@ const struct rtw89_chan *__rtw89_mgnt_ch + #define rtw89_mgnt_chan_get(rtwdev, link_index) \ + __rtw89_mgnt_chan_get(rtwdev, __func__, link_index) + ++void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work); ++ + int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, + struct ieee80211_chanctx_conf *ctx); + void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, +--- a/drivers/net/wireless/realtek/rtw89/core.c ++++ b/drivers/net/wireless/realtek/rtw89/core.c +@@ -4816,6 +4816,7 @@ void rtw89_core_stop(struct rtw89_dev *r + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_rfk_chk_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->cfo_track_work); ++ wiphy_delayed_work_cancel(wiphy, &rtwdev->mcc_prepare_done_work); + cancel_delayed_work_sync(&rtwdev->forbid_ba_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work); + +@@ -5042,6 +5043,7 @@ int rtw89_core_init(struct rtw89_dev *rt + wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); + wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); + wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); ++ wiphy_delayed_work_init(&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work); + INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); + wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); + rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); +--- a/drivers/net/wireless/realtek/rtw89/core.h ++++ b/drivers/net/wireless/realtek/rtw89/core.h +@@ -5728,6 +5728,7 @@ struct rtw89_mcc_config { + struct rtw89_mcc_sync sync; + u64 start_tsf; + u64 start_tsf_in_aux_domain; ++ u64 prepare_delay; + u16 mcc_interval; /* TU */ + u16 beacon_offset; /* TU */ + }; +@@ -5858,6 +5859,7 @@ struct rtw89_dev { + struct wiphy_delayed_work coex_bt_devinfo_work; + struct wiphy_delayed_work coex_rfk_chk_work; + struct wiphy_delayed_work cfo_track_work; ++ struct wiphy_delayed_work mcc_prepare_done_work; + struct delayed_work forbid_ba_work; + struct wiphy_delayed_work antdiv_work; + struct rtw89_ppdu_sts_info ppdu_sts;