Commit 7dae85b5 authored by Larry Finger's avatar Larry Finger Committed by Greg Kroah-Hartman

rtlwifi: Fix enter/exit power_save

commit ba9f93f8 upstream.

In commit a5ffbe0a ("rtlwifi: Fix scheduling while atomic bug") and
commit a269913c ("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: a269913c ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue")
Reported-by: default avatarPing-Ke Shih <pkshih@realtek.com>
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Cc: Stable <stable@vger.kernel.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3f41ee3a
...@@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc); ...@@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc);
static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc) 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; rtlpriv->ra.is_special_data = true;
if (rtlpriv->cfg->ops->get_btc_status()) if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_special_packet_notify( rtlpriv->btcoexist.btc_ops->btc_special_packet_notify(
rtlpriv, 1); rtlpriv, 1);
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
ppsc->last_delaylps_stamp_jiffies = jiffies; ppsc->last_delaylps_stamp_jiffies = jiffies;
} }
...@@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx, ...@@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
if (is_tx) { if (is_tx) {
rtlpriv->ra.is_special_data = true; rtlpriv->ra.is_special_data = true;
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
ppsc->last_delaylps_stamp_jiffies = jiffies; ppsc->last_delaylps_stamp_jiffies = jiffies;
} }
......
...@@ -1150,10 +1150,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1150,10 +1150,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
} else { } else {
mstatus = RT_MEDIA_DISCONNECT; mstatus = RT_MEDIA_DISCONNECT;
if (mac->link_state == MAC80211_LINKED) { if (mac->link_state == MAC80211_LINKED)
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
}
if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
mac->link_state = MAC80211_NOLINK; mac->link_state = MAC80211_NOLINK;
...@@ -1431,8 +1429,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw, ...@@ -1431,8 +1429,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
} }
if (mac->link_state == MAC80211_LINKED) { if (mac->link_state == MAC80211_LINKED) {
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
mac->link_state = MAC80211_LINKED_SCANNING; mac->link_state = MAC80211_LINKED_SCANNING;
} else { } else {
rtl_ips_nic_on(hw); rtl_ips_nic_on(hw);
......
...@@ -663,11 +663,9 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) ...@@ -663,11 +663,9 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio)
} }
if (((rtlpriv->link_info.num_rx_inperiod + if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) || rtlpriv->link_info.num_tx_inperiod) > 8) ||
(rtlpriv->link_info.num_rx_inperiod > 2)) { (rtlpriv->link_info.num_rx_inperiod > 2))
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
}
} }
static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
...@@ -918,10 +916,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) ...@@ -918,10 +916,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
} }
if (((rtlpriv->link_info.num_rx_inperiod + if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) || rtlpriv->link_info.num_tx_inperiod) > 8) ||
(rtlpriv->link_info.num_rx_inperiod > 2)) { (rtlpriv->link_info.num_rx_inperiod > 2))
rtlpriv->enter_ps = false; rtl_lps_leave(hw);
schedule_work(&rtlpriv->works.lps_change_work);
}
skb = new_skb; skb = new_skb;
no_new: no_new:
if (rtlpriv->use_new_trx_flow) { if (rtlpriv->use_new_trx_flow) {
......
...@@ -407,8 +407,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) ...@@ -407,8 +407,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
} }
} }
/*Enter the leisure power save mode.*/ /* Interrupt safe routine to enter the leisure power save mode.*/
void rtl_lps_enter(struct ieee80211_hw *hw) static void rtl_lps_enter_core(struct ieee80211_hw *hw)
{ {
struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
...@@ -444,10 +444,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw) ...@@ -444,10 +444,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
} }
EXPORT_SYMBOL(rtl_lps_enter);
/*Leave the leisure power save mode.*/ /* Interrupt safe routine to leave the leisure power save mode.*/
void rtl_lps_leave(struct ieee80211_hw *hw) static void rtl_lps_leave_core(struct ieee80211_hw *hw)
{ {
struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
...@@ -477,7 +476,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw) ...@@ -477,7 +476,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw)
} }
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
} }
EXPORT_SYMBOL(rtl_lps_leave);
/* For sw LPS*/ /* For sw LPS*/
void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
...@@ -670,12 +668,34 @@ void rtl_lps_change_work_callback(struct work_struct *work) ...@@ -670,12 +668,34 @@ void rtl_lps_change_work_callback(struct work_struct *work)
struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_priv *rtlpriv = rtl_priv(hw);
if (rtlpriv->enter_ps) if (rtlpriv->enter_ps)
rtl_lps_enter(hw); rtl_lps_enter_core(hw);
else else
rtl_lps_leave(hw); rtl_lps_leave_core(hw);
} }
EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); 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) void rtl_swlps_wq_callback(void *data)
{ {
struct rtl_works *rtlworks = container_of_dwork_rtl(data, struct rtl_works *rtlworks = container_of_dwork_rtl(data,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment