]> git.karo-electronics.de Git - linux-beck.git/commitdiff
rtlwifi: Fix enter/exit power_save
authorLarry Finger <Larry.Finger@lwfinger.net>
Sat, 26 Nov 2016 20:43:35 +0000 (14:43 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Jan 2017 07:32:16 +0000 (08:32 +0100)
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
drivers/net/wireless/realtek/rtlwifi/core.c
drivers/net/wireless/realtek/rtlwifi/pci.c
drivers/net/wireless/realtek/rtlwifi/ps.c

index 264466f59c57029e71c64109b58c0b359557f5fe..4ac928bf1f8e6c77f68386c353c20f26168c77e4 100644 (file)
@@ -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_hw *hw, struct sk_buff *skb, u8 is_tx,
 
                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;
                }
 
index 8e7f23c11680a5fa98352e5052f02fa9ae091de8..4da4e458142c46247f1d71008a74acb6d8b8ef90 100644 (file)
@@ -1150,10 +1150,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
                } 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;
@@ -1431,8 +1429,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
        }
 
        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);
index 0dfa9eac3926dd488262529cd536c23c7eea08be..5be4fc96002d1f64d98271c4c8313793f2c0251e 100644 (file)
@@ -663,11 +663,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,
@@ -918,10 +916,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) {
index 18d979affc1861ebc97dad9fe083d7159d40b746..d0ffc4d508cff2df70606c9a88845a22884c7bc9 100644 (file)
@@ -407,8 +407,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
        }
 }
 
-/*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 *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 *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 work_struct *work)
        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,