aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPing-Ke Shih <pkshih@realtek.com>2018-06-22 01:31:57 -0400
committerKalle Valo <kvalo@codeaurora.org>2018-06-24 12:54:21 -0400
commit12dfa2f68ab659636e092db13b5d17cf9aac82af (patch)
tree1e1910c37a63fde14b295d4e5773c3c882db41e4
parent755abd247a3da273af3b90d1dd31bc61794ae7d7 (diff)
rtlwifi: Fix kernel Oops "Fw download fail!!"
When connecting to AP, mac80211 asks driver to enter and leave PS quickly, but driver deinit doesn't wait for delayed work complete when entering PS, then driver reinit procedure and delay work are running simultaneously. This will cause unpredictable kernel oops or crash like rtl8723be: error H2C cmd because of Fw download fail!!! WARNING: CPU: 3 PID: 159 at drivers/net/wireless/realtek/rtlwifi/ rtl8723be/fw.c:227 rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be] CPU: 3 PID: 159 Comm: kworker/3:2 Tainted: G O 4.16.13-2-ARCH #1 Hardware name: ASUSTeK COMPUTER INC. X556UF/X556UF, BIOS X556UF.406 10/21/2016 Workqueue: rtl8723be_pci rtl_c2hcmd_wq_callback [rtlwifi] RIP: 0010:rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be] RSP: 0018:ffffa6ab01e1bd70 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffffa26069071520 RCX: 0000000000000001 RDX: 0000000080000001 RSI: ffffffff8be70e9c RDI: 00000000ffffffff RBP: 0000000000000000 R08: 0000000000000048 R09: 0000000000000348 R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000 R13: ffffa26069071520 R14: 0000000000000000 R15: ffffa2607d205f70 FS: 0000000000000000(0000) GS:ffffa26081d80000(0000) knlGS:000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000443b39d3000 CR3: 000000037700a005 CR4: 00000000003606e0 Call Trace: ? halbtc_send_bt_mp_operation.constprop.17+0xd5/0xe0 [btcoexist] ? ex_btc8723b1ant_bt_info_notify+0x3b8/0x820 [btcoexist] ? rtl_c2hcmd_launcher+0xab/0x110 [rtlwifi] ? process_one_work+0x1d1/0x3b0 ? worker_thread+0x2b/0x3d0 ? process_one_work+0x3b0/0x3b0 ? kthread+0x112/0x130 ? kthread_create_on_node+0x60/0x60 ? ret_from_fork+0x35/0x40 Code: 00 76 b4 e9 e2 fe ff ff 4c 89 ee 4c 89 e7 e8 56 22 86 ca e9 5e ... This patch ensures all delayed works done before entering PS to satisfy our expectation, so use cancel_delayed_work_sync() instead. An exception is delayed work ips_nic_off_wq because running task may be itself, so add a parameter ips_wq to deinit function to handle this case. This issue is reported and fixed in below threads: https://github.com/lwfinger/rtlwifi_new/issues/367 https://github.com/lwfinger/rtlwifi_new/issues/366 Tested-by: Evgeny Kapun <abacabadabacaba@gmail.com> # 8723DE Tested-by: Shivam Kakkar <shivam543@gmail.com> # 8723BE on 4.18-rc1 Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Fixes: cceb0a597320 ("rtlwifi: Add work queue for c2h cmd.") Cc: Stable <stable@vger.kernel.org> # 4.11+ Reviewed-by: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c17
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.h2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/ps.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c2
6 files changed, 16 insertions, 13 deletions
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index 39c817eddd78..54c9f6ab0c8c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -484,18 +484,21 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw)
484 484
485} 485}
486 486
487void rtl_deinit_deferred_work(struct ieee80211_hw *hw) 487void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq)
488{ 488{
489 struct rtl_priv *rtlpriv = rtl_priv(hw); 489 struct rtl_priv *rtlpriv = rtl_priv(hw);
490 490
491 del_timer_sync(&rtlpriv->works.watchdog_timer); 491 del_timer_sync(&rtlpriv->works.watchdog_timer);
492 492
493 cancel_delayed_work(&rtlpriv->works.watchdog_wq); 493 cancel_delayed_work_sync(&rtlpriv->works.watchdog_wq);
494 cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); 494 if (ips_wq)
495 cancel_delayed_work(&rtlpriv->works.ps_work); 495 cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq);
496 cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); 496 else
497 cancel_delayed_work(&rtlpriv->works.fwevt_wq); 497 cancel_delayed_work_sync(&rtlpriv->works.ips_nic_off_wq);
498 cancel_delayed_work(&rtlpriv->works.c2hcmd_wq); 498 cancel_delayed_work_sync(&rtlpriv->works.ps_work);
499 cancel_delayed_work_sync(&rtlpriv->works.ps_rfon_wq);
500 cancel_delayed_work_sync(&rtlpriv->works.fwevt_wq);
501 cancel_delayed_work_sync(&rtlpriv->works.c2hcmd_wq);
499} 502}
500EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work); 503EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work);
501 504
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h b/drivers/net/wireless/realtek/rtlwifi/base.h
index 912f205779c3..a7ae40eaa3cd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.h
+++ b/drivers/net/wireless/realtek/rtlwifi/base.h
@@ -121,7 +121,7 @@ void rtl_init_rfkill(struct ieee80211_hw *hw);
121void rtl_deinit_rfkill(struct ieee80211_hw *hw); 121void rtl_deinit_rfkill(struct ieee80211_hw *hw);
122 122
123void rtl_watch_dog_timer_callback(struct timer_list *t); 123void rtl_watch_dog_timer_callback(struct timer_list *t);
124void rtl_deinit_deferred_work(struct ieee80211_hw *hw); 124void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq);
125 125
126bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); 126bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
127int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, 127int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index cfea57efa7f4..a3f46203ee7a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -196,7 +196,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw)
196 /* reset sec info */ 196 /* reset sec info */
197 rtl_cam_reset_sec_info(hw); 197 rtl_cam_reset_sec_info(hw);
198 198
199 rtl_deinit_deferred_work(hw); 199 rtl_deinit_deferred_work(hw, false);
200 } 200 }
201 rtlpriv->intf_ops->adapter_stop(hw); 201 rtlpriv->intf_ops->adapter_stop(hw);
202 202
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index ae13bcfb3bf0..5d1fda16fc8c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -2377,7 +2377,7 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
2377 ieee80211_unregister_hw(hw); 2377 ieee80211_unregister_hw(hw);
2378 rtlmac->mac80211_registered = 0; 2378 rtlmac->mac80211_registered = 0;
2379 } else { 2379 } else {
2380 rtl_deinit_deferred_work(hw); 2380 rtl_deinit_deferred_work(hw, false);
2381 rtlpriv->intf_ops->adapter_stop(hw); 2381 rtlpriv->intf_ops->adapter_stop(hw);
2382 } 2382 }
2383 rtlpriv->cfg->ops->disable_interrupt(hw); 2383 rtlpriv->cfg->ops->disable_interrupt(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c
index 71af24e2e051..479a4cfc245d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/ps.c
+++ b/drivers/net/wireless/realtek/rtlwifi/ps.c
@@ -71,7 +71,7 @@ bool rtl_ps_disable_nic(struct ieee80211_hw *hw)
71 struct rtl_priv *rtlpriv = rtl_priv(hw); 71 struct rtl_priv *rtlpriv = rtl_priv(hw);
72 72
73 /*<1> Stop all timer */ 73 /*<1> Stop all timer */
74 rtl_deinit_deferred_work(hw); 74 rtl_deinit_deferred_work(hw, true);
75 75
76 /*<2> Disable Interrupt */ 76 /*<2> Disable Interrupt */
77 rtlpriv->cfg->ops->disable_interrupt(hw); 77 rtlpriv->cfg->ops->disable_interrupt(hw);
@@ -292,7 +292,7 @@ void rtl_ips_nic_on(struct ieee80211_hw *hw)
292 struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 292 struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
293 enum rf_pwrstate rtstate; 293 enum rf_pwrstate rtstate;
294 294
295 cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); 295 cancel_delayed_work_sync(&rtlpriv->works.ips_nic_off_wq);
296 296
297 mutex_lock(&rtlpriv->locks.ips_mutex); 297 mutex_lock(&rtlpriv->locks.ips_mutex);
298 if (ppsc->inactiveps) { 298 if (ppsc->inactiveps) {
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index f9faffc498bc..2ac5004d7a40 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -1132,7 +1132,7 @@ void rtl_usb_disconnect(struct usb_interface *intf)
1132 ieee80211_unregister_hw(hw); 1132 ieee80211_unregister_hw(hw);
1133 rtlmac->mac80211_registered = 0; 1133 rtlmac->mac80211_registered = 0;
1134 } else { 1134 } else {
1135 rtl_deinit_deferred_work(hw); 1135 rtl_deinit_deferred_work(hw, false);
1136 rtlpriv->intf_ops->adapter_stop(hw); 1136 rtlpriv->intf_ops->adapter_stop(hw);
1137 } 1137 }
1138 /*deinit rfkill */ 1138 /*deinit rfkill */