| From ba9f93f82abafe2552eac942ebb11c2df4f8dd7f Mon Sep 17 00:00:00 2001 |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| Date: Sat, 26 Nov 2016 14:43:35 -0600 |
| Subject: rtlwifi: Fix enter/exit power_save |
| |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| |
| commit ba9f93f82abafe2552eac942ebb11c2df4f8dd7f upstream. |
| |
| In commit a5ffbe0a1993 ("rtlwifi: Fix scheduling while atomic bug") and |
| commit a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() |
| to use work queue"), an error was introduced in the power-save routines |
| due to the fact that leaving PS was delayed by the use of a work queue. |
| |
| This problem is fixed by detecting if the enter or leave routines are |
| in interrupt mode. If so, the workqueue is used to place the request. |
| If in normal mode, the enter or leave routines are called directly. |
| |
| Fixes: a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue") |
| Reported-by: Ping-Ke Shih <pkshih@realtek.com> |
| Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/realtek/rtlwifi/base.c | 8 +++--- |
| drivers/net/wireless/realtek/rtlwifi/core.c | 9 ++----- |
| drivers/net/wireless/realtek/rtlwifi/pci.c | 14 +++------- |
| drivers/net/wireless/realtek/rtlwifi/ps.c | 36 +++++++++++++++++++++------- |
| 4 files changed, 40 insertions(+), 27 deletions(-) |
| |
| --- a/drivers/net/wireless/realtek/rtlwifi/base.c |
| +++ b/drivers/net/wireless/realtek/rtlwifi/base.c |
| @@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc); |
| |
| static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) |
| { |
| + struct ieee80211_hw *hw = rtlpriv->hw; |
| + |
| rtlpriv->ra.is_special_data = true; |
| if (rtlpriv->cfg->ops->get_btc_status()) |
| rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( |
| rtlpriv, 1); |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| + rtl_lps_leave(hw); |
| ppsc->last_delaylps_stamp_jiffies = jiffies; |
| } |
| |
| @@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_ |
| |
| if (is_tx) { |
| rtlpriv->ra.is_special_data = true; |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| + rtl_lps_leave(hw); |
| ppsc->last_delaylps_stamp_jiffies = jiffies; |
| } |
| |
| --- a/drivers/net/wireless/realtek/rtlwifi/core.c |
| +++ b/drivers/net/wireless/realtek/rtlwifi/core.c |
| @@ -1149,10 +1149,8 @@ static void rtl_op_bss_info_changed(stru |
| } else { |
| mstatus = RT_MEDIA_DISCONNECT; |
| |
| - if (mac->link_state == MAC80211_LINKED) { |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| - } |
| + if (mac->link_state == MAC80211_LINKED) |
| + rtl_lps_leave(hw); |
| if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) |
| rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); |
| mac->link_state = MAC80211_NOLINK; |
| @@ -1430,8 +1428,7 @@ static void rtl_op_sw_scan_start(struct |
| } |
| |
| if (mac->link_state == MAC80211_LINKED) { |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| + rtl_lps_leave(hw); |
| mac->link_state = MAC80211_LINKED_SCANNING; |
| } else { |
| rtl_ips_nic_on(hw); |
| --- a/drivers/net/wireless/realtek/rtlwifi/pci.c |
| +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c |
| @@ -662,11 +662,9 @@ tx_status_ok: |
| } |
| |
| if (((rtlpriv->link_info.num_rx_inperiod + |
| - rtlpriv->link_info.num_tx_inperiod) > 8) || |
| - (rtlpriv->link_info.num_rx_inperiod > 2)) { |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| - } |
| + rtlpriv->link_info.num_tx_inperiod) > 8) || |
| + (rtlpriv->link_info.num_rx_inperiod > 2)) |
| + rtl_lps_leave(hw); |
| } |
| |
| static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, |
| @@ -917,10 +915,8 @@ new_trx_end: |
| } |
| if (((rtlpriv->link_info.num_rx_inperiod + |
| rtlpriv->link_info.num_tx_inperiod) > 8) || |
| - (rtlpriv->link_info.num_rx_inperiod > 2)) { |
| - rtlpriv->enter_ps = false; |
| - schedule_work(&rtlpriv->works.lps_change_work); |
| - } |
| + (rtlpriv->link_info.num_rx_inperiod > 2)) |
| + rtl_lps_leave(hw); |
| skb = new_skb; |
| no_new: |
| if (rtlpriv->use_new_trx_flow) { |
| --- a/drivers/net/wireless/realtek/rtlwifi/ps.c |
| +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c |
| @@ -407,8 +407,8 @@ void rtl_lps_set_psmode(struct ieee80211 |
| } |
| } |
| |
| -/*Enter the leisure power save mode.*/ |
| -void rtl_lps_enter(struct ieee80211_hw *hw) |
| +/* Interrupt safe routine to enter the leisure power save mode.*/ |
| +static void rtl_lps_enter_core(struct ieee80211_hw *hw) |
| { |
| struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); |
| struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); |
| @@ -444,10 +444,9 @@ void rtl_lps_enter(struct ieee80211_hw * |
| |
| spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); |
| } |
| -EXPORT_SYMBOL(rtl_lps_enter); |
| |
| -/*Leave the leisure power save mode.*/ |
| -void rtl_lps_leave(struct ieee80211_hw *hw) |
| +/* Interrupt safe routine to leave the leisure power save mode.*/ |
| +static void rtl_lps_leave_core(struct ieee80211_hw *hw) |
| { |
| struct rtl_priv *rtlpriv = rtl_priv(hw); |
| struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); |
| @@ -477,7 +476,6 @@ void rtl_lps_leave(struct ieee80211_hw * |
| } |
| spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); |
| } |
| -EXPORT_SYMBOL(rtl_lps_leave); |
| |
| /* For sw LPS*/ |
| void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) |
| @@ -670,12 +668,34 @@ void rtl_lps_change_work_callback(struct |
| struct rtl_priv *rtlpriv = rtl_priv(hw); |
| |
| if (rtlpriv->enter_ps) |
| - rtl_lps_enter(hw); |
| + rtl_lps_enter_core(hw); |
| else |
| - rtl_lps_leave(hw); |
| + rtl_lps_leave_core(hw); |
| } |
| EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); |
| |
| +void rtl_lps_enter(struct ieee80211_hw *hw) |
| +{ |
| + struct rtl_priv *rtlpriv = rtl_priv(hw); |
| + |
| + if (!in_interrupt()) |
| + return rtl_lps_enter_core(hw); |
| + rtlpriv->enter_ps = true; |
| + schedule_work(&rtlpriv->works.lps_change_work); |
| +} |
| +EXPORT_SYMBOL_GPL(rtl_lps_enter); |
| + |
| +void rtl_lps_leave(struct ieee80211_hw *hw) |
| +{ |
| + struct rtl_priv *rtlpriv = rtl_priv(hw); |
| + |
| + if (!in_interrupt()) |
| + return rtl_lps_leave_core(hw); |
| + rtlpriv->enter_ps = false; |
| + schedule_work(&rtlpriv->works.lps_change_work); |
| +} |
| +EXPORT_SYMBOL_GPL(rtl_lps_leave); |
| + |
| void rtl_swlps_wq_callback(void *data) |
| { |
| struct rtl_works *rtlworks = container_of_dwork_rtl(data, |