wl12xx: fix race between suspend/resume and recovery
authorEyal Shapira <eyal@wizery.com>
Tue, 13 Mar 2012 18:03:21 +0000 (20:03 +0200)
committerLuciano Coelho <coelho@ti.com>
Tue, 10 Apr 2012 09:13:58 +0000 (12:13 +0300)
The iteration on the wlvif list in wl1271_op_resume/suspend was
perfomed before locking wl->mutex which would lead to a kernel
panic in case a recovery was queued at the same time
and would delete the wlvifs from the list.

Signed-off-by: Eyal Shapira <eyal@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/main.c

index a1ede7b48270cb05809f0891804eed6249a7ec29..13820437806eca5cafadc0925f3559b855b47d09 100644 (file)
@@ -1652,14 +1652,12 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
 {
        int ret = 0;
 
-       mutex_lock(&wl->mutex);
-
        if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-               goto out_unlock;
+               goto out;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
-               goto out_unlock;
+               goto out;
 
        ret = wl1271_acx_wake_up_conditions(wl, wlvif,
                                    wl->conf.conn.suspend_wake_up_event,
@@ -1668,11 +1666,9 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
        if (ret < 0)
                wl1271_error("suspend: set wake up conditions failed: %d", ret);
 
-
        wl1271_ps_elp_sleep(wl);
 
-out_unlock:
-       mutex_unlock(&wl->mutex);
+out:
        return ret;
 
 }
@@ -1682,20 +1678,17 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl,
 {
        int ret = 0;
 
-       mutex_lock(&wl->mutex);
-
        if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
-               goto out_unlock;
+               goto out;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
-               goto out_unlock;
+               goto out;
 
        ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
 
        wl1271_ps_elp_sleep(wl);
-out_unlock:
-       mutex_unlock(&wl->mutex);
+out:
        return ret;
 
 }
@@ -1720,10 +1713,9 @@ static void wl1271_configure_resume(struct wl1271 *wl,
        if ((!is_ap) && (!is_sta))
                return;
 
-       mutex_lock(&wl->mutex);
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
-               goto out;
+               return;
 
        if (is_sta) {
                ret = wl1271_acx_wake_up_conditions(wl, wlvif,
@@ -1739,8 +1731,6 @@ static void wl1271_configure_resume(struct wl1271 *wl,
        }
 
        wl1271_ps_elp_sleep(wl);
-out:
-       mutex_unlock(&wl->mutex);
 }
 
 static int wl1271_op_suspend(struct ieee80211_hw *hw,
@@ -1755,6 +1745,7 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
 
        wl1271_tx_flush(wl);
 
+       mutex_lock(&wl->mutex);
        wl->wow_enabled = true;
        wl12xx_for_each_wlvif(wl, wlvif) {
                ret = wl1271_configure_suspend(wl, wlvif);
@@ -1763,6 +1754,7 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
                        return ret;
                }
        }
+       mutex_unlock(&wl->mutex);
        /* flush any remaining work */
        wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
 
@@ -1812,10 +1804,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
                wl1271_irq(0, wl);
                wl1271_enable_interrupts(wl);
        }
+
+       mutex_lock(&wl->mutex);
        wl12xx_for_each_wlvif(wl, wlvif) {
                wl1271_configure_resume(wl, wlvif);
        }
        wl->wow_enabled = false;
+       mutex_unlock(&wl->mutex);
 
        return 0;
 }