Message ID | 20180622053157.6155-1-pkshih@realtek.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 12dfa2f68ab659636e092db13b5d17cf9aac82af |
Delegated to: | Kalle Valo |
Headers | show |
Ping-Ke Shih <pkshih@realtek.com> wrote: > From: Ping-Ke Shih <pkshih@realtek.com> > > 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> Patch applied to wireless-drivers.git, thanks. 12dfa2f68ab6 rtlwifi: Fix kernel Oops "Fw download fail!!"
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) } -void rtl_deinit_deferred_work(struct ieee80211_hw *hw) +void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq) { struct rtl_priv *rtlpriv = rtl_priv(hw); del_timer_sync(&rtlpriv->works.watchdog_timer); - cancel_delayed_work(&rtlpriv->works.watchdog_wq); - cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); - cancel_delayed_work(&rtlpriv->works.ps_work); - cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); - cancel_delayed_work(&rtlpriv->works.fwevt_wq); - cancel_delayed_work(&rtlpriv->works.c2hcmd_wq); + cancel_delayed_work_sync(&rtlpriv->works.watchdog_wq); + if (ips_wq) + cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); + else + cancel_delayed_work_sync(&rtlpriv->works.ips_nic_off_wq); + cancel_delayed_work_sync(&rtlpriv->works.ps_work); + cancel_delayed_work_sync(&rtlpriv->works.ps_rfon_wq); + cancel_delayed_work_sync(&rtlpriv->works.fwevt_wq); + cancel_delayed_work_sync(&rtlpriv->works.c2hcmd_wq); } EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work); 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); void rtl_deinit_rfkill(struct ieee80211_hw *hw); void rtl_watch_dog_timer_callback(struct timer_list *t); -void rtl_deinit_deferred_work(struct ieee80211_hw *hw); +void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); int 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) /* reset sec info */ rtl_cam_reset_sec_info(hw); - rtl_deinit_deferred_work(hw); + rtl_deinit_deferred_work(hw, false); } rtlpriv->intf_ops->adapter_stop(hw); 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) ieee80211_unregister_hw(hw); rtlmac->mac80211_registered = 0; } else { - rtl_deinit_deferred_work(hw); + rtl_deinit_deferred_work(hw, false); rtlpriv->intf_ops->adapter_stop(hw); } 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) struct rtl_priv *rtlpriv = rtl_priv(hw); /*<1> Stop all timer */ - rtl_deinit_deferred_work(hw); + rtl_deinit_deferred_work(hw, true); /*<2> Disable Interrupt */ rtlpriv->cfg->ops->disable_interrupt(hw); @@ -292,7 +292,7 @@ void rtl_ips_nic_on(struct ieee80211_hw *hw) struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); enum rf_pwrstate rtstate; - cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); + cancel_delayed_work_sync(&rtlpriv->works.ips_nic_off_wq); mutex_lock(&rtlpriv->locks.ips_mutex); 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) ieee80211_unregister_hw(hw); rtlmac->mac80211_registered = 0; } else { - rtl_deinit_deferred_work(hw); + rtl_deinit_deferred_work(hw, false); rtlpriv->intf_ops->adapter_stop(hw); } /*deinit rfkill */