--- /dev/null
+From 53bc1b73b67836ac9867f93dee7a443986b4a94f Mon Sep 17 00:00:00 2001
+From: Ping-Ke Shih <pkshih@realtek.com>
+Date: Thu, 22 Aug 2024 09:42:54 +0800
+Subject: wifi: mac80211: export ieee80211_purge_tx_queue() for drivers
+
+From: Ping-Ke Shih <pkshih@realtek.com>
+
+commit 53bc1b73b67836ac9867f93dee7a443986b4a94f upstream.
+
+Drivers need to purge TX SKB when stopping. Using skb_queue_purge() can't
+report TX status to mac80211, causing ieee80211_free_ack_frame() warns
+"Have pending ack frames!". Export ieee80211_purge_tx_queue() for drivers
+to not have to reimplement it.
+
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/20240822014255.10211-1-pkshih@realtek.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/mac80211.h | 13 +++++++++++++
+ net/mac80211/ieee80211_i.h | 2 --
+ net/mac80211/status.c | 1 +
+ 3 files changed, 14 insertions(+), 2 deletions(-)
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -2957,6 +2957,19 @@ ieee80211_get_alt_retry_rate(const struc
+ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
+
+ /**
++ * ieee80211_purge_tx_queue - purge TX skb queue
++ * @hw: the hardware
++ * @skbs: the skbs
++ *
++ * Free a set of transmit skbs. Use this function when device is going to stop
++ * but some transmit skbs without TX status are still queued.
++ * This function does not take the list lock and the caller must hold the
++ * relevant locks to use it.
++ */
++void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
++ struct sk_buff_head *skbs);
++
++/**
+ * DOC: Hardware crypto acceleration
+ *
+ * mac80211 is capable of taking advantage of many hardware
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1984,8 +1984,6 @@ void __ieee80211_subif_start_xmit(struct
+ u32 info_flags,
+ u32 ctrl_flags,
+ u64 *cookie);
+-void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+- struct sk_buff_head *skbs);
+ struct sk_buff *
+ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags);
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1294,3 +1294,4 @@ void ieee80211_purge_tx_queue(struct iee
+ while ((skb = __skb_dequeue(skbs)))
+ ieee80211_free_txskb(hw, skb);
+ }
++EXPORT_SYMBOL(ieee80211_purge_tx_queue);
--- /dev/null
+From 3e5e4a801aaf4283390cc34959c6c48f910ca5ea Mon Sep 17 00:00:00 2001
+From: Ping-Ke Shih <pkshih@realtek.com>
+Date: Thu, 22 Aug 2024 09:42:55 +0800
+Subject: wifi: rtw88: use ieee80211_purge_tx_queue() to purge TX skb
+
+From: Ping-Ke Shih <pkshih@realtek.com>
+
+commit 3e5e4a801aaf4283390cc34959c6c48f910ca5ea upstream.
+
+When removing kernel modules by:
+ rmmod rtw88_8723cs rtw88_8703b rtw88_8723x rtw88_sdio rtw88_core
+
+Driver uses skb_queue_purge() to purge TX skb, but not report tx status
+causing "Have pending ack frames!" warning. Use ieee80211_purge_tx_queue()
+to correct this.
+
+Since ieee80211_purge_tx_queue() doesn't take locks, to prevent racing
+between TX work and purge TX queue, flush and destroy TX work in advance.
+
+ wlan0: deauthenticating from aa:f5:fd:60:4c:a8 by local
+ choice (Reason: 3=DEAUTH_LEAVING)
+ ------------[ cut here ]------------
+ Have pending ack frames!
+ WARNING: CPU: 3 PID: 9232 at net/mac80211/main.c:1691
+ ieee80211_free_ack_frame+0x5c/0x90 [mac80211]
+ CPU: 3 PID: 9232 Comm: rmmod Tainted: G C
+ 6.10.1-200.fc40.aarch64 #1
+ Hardware name: pine64 Pine64 PinePhone Braveheart
+ (1.1)/Pine64 PinePhone Braveheart (1.1), BIOS 2024.01 01/01/2024
+ pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+ pc : ieee80211_free_ack_frame+0x5c/0x90 [mac80211]
+ lr : ieee80211_free_ack_frame+0x5c/0x90 [mac80211]
+ sp : ffff80008c1b37b0
+ x29: ffff80008c1b37b0 x28: ffff000003be8000 x27: 0000000000000000
+ x26: 0000000000000000 x25: ffff000003dc14b8 x24: ffff80008c1b37d0
+ x23: ffff000000ff9f80 x22: 0000000000000000 x21: 000000007fffffff
+ x20: ffff80007c7e93d8 x19: ffff00006e66f400 x18: 0000000000000000
+ x17: ffff7ffffd2b3000 x16: ffff800083fc0000 x15: 0000000000000000
+ x14: 0000000000000000 x13: 2173656d61726620 x12: 6b636120676e6964
+ x11: 0000000000000000 x10: 000000000000005d x9 : ffff8000802af2b0
+ x8 : ffff80008c1b3430 x7 : 0000000000000001 x6 : 0000000000000001
+ x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
+ x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000003be8000
+ Call trace:
+ ieee80211_free_ack_frame+0x5c/0x90 [mac80211]
+ idr_for_each+0x74/0x110
+ ieee80211_free_hw+0x44/0xe8 [mac80211]
+ rtw_sdio_remove+0x9c/0xc0 [rtw88_sdio]
+ sdio_bus_remove+0x44/0x180
+ device_remove+0x54/0x90
+ device_release_driver_internal+0x1d4/0x238
+ driver_detach+0x54/0xc0
+ bus_remove_driver+0x78/0x108
+ driver_unregister+0x38/0x78
+ sdio_unregister_driver+0x2c/0x40
+ rtw_8723cs_driver_exit+0x18/0x1000 [rtw88_8723cs]
+ __do_sys_delete_module.isra.0+0x190/0x338
+ __arm64_sys_delete_module+0x1c/0x30
+ invoke_syscall+0x74/0x100
+ el0_svc_common.constprop.0+0x48/0xf0
+ do_el0_svc+0x24/0x38
+ el0_svc+0x3c/0x158
+ el0t_64_sync_handler+0x120/0x138
+ el0t_64_sync+0x194/0x198
+ ---[ end trace 0000000000000000 ]---
+
+Reported-by: Peter Robinson <pbrobinson@gmail.com>
+Closes: https://lore.kernel.org/linux-wireless/CALeDE9OAa56KMzgknaCD3quOgYuEHFx9_hcT=OFgmMAb+8MPyA@mail.gmail.com/
+Tested-by: Ping-Ke Shih <pkshih@realtek.com> # 8723DU
+Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
+Link: https://patch.msgid.link/20240822014255.10211-2-pkshih@realtek.com
+Signed-off-by: Ladislav Michl <ladis@triops.cz>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/realtek/rtw88/main.c | 2 +-
+ drivers/net/wireless/realtek/rtw88/tx.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/wireless/realtek/rtw88/main.c
++++ b/drivers/net/wireless/realtek/rtw88/main.c
+@@ -2146,7 +2146,7 @@ void rtw_core_deinit(struct rtw_dev *rtw
+
+ destroy_workqueue(rtwdev->tx_wq);
+ spin_lock_irqsave(&rtwdev->tx_report.q_lock, flags);
+- skb_queue_purge(&rtwdev->tx_report.queue);
++ ieee80211_purge_tx_queue(rtwdev->hw, &rtwdev->tx_report.queue);
+ skb_queue_purge(&rtwdev->coex.queue);
+ spin_unlock_irqrestore(&rtwdev->tx_report.q_lock, flags);
+
+--- a/drivers/net/wireless/realtek/rtw88/tx.c
++++ b/drivers/net/wireless/realtek/rtw88/tx.c
+@@ -169,7 +169,7 @@ void rtw_tx_report_purge_timer(struct ti
+ rtw_warn(rtwdev, "failed to get tx report from firmware\n");
+
+ spin_lock_irqsave(&tx_report->q_lock, flags);
+- skb_queue_purge(&tx_report->queue);
++ ieee80211_purge_tx_queue(rtwdev->hw, &tx_report->queue);
+ spin_unlock_irqrestore(&tx_report->q_lock, flags);
+ }
+