aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorIdo Yariv <ido@wizery.com>2012-06-26 14:08:58 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-07-10 12:10:14 -0400
commitc24ec83bcac50160c344b3eb0e4c601b6cb4f7e5 (patch)
treebdc42304657804346a0254c780a8572157f2cd31 /drivers
parentd8ae5a257c81e3977cbd2a50f190a601f51d3bdd (diff)
wlcore: Prevent processing of work items during op_stop
The interrupt line is disabled in op_stop using disable_irq. Since pending interrupts are synchronized, the mutex has to be released before disabling the interrupt to avoid a deadlock with the interrupt handler. In addition, the internal state of the driver is only set to 'off' after the interrupt is disabled. Otherwise, if an interrupt fires after the state is set but before the interrupt line is disabled, the interrupt handler will not be able to acknowledge the interrupt resulting in an interrupt storm. The driver's operations might be called during recovery. If these acquire the mutex after it was released by op_stop, but before the driver's state is changed, they may queue new work items instead of just failing. This is especially problematic in the case of scans, in which a new scan may be scheduled after all scan requests were cancelled. Signed-off-by: Ido Yariv <ido@wizery.com> Signed-off-by: Arik Nemtsov <arik@wizery.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ti/wlcore/io.c6
-rw-r--r--drivers/net/wireless/ti/wlcore/io.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c50
3 files changed, 32 insertions, 25 deletions
diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c
index 9976219c4e49..68e74eefd296 100644
--- a/drivers/net/wireless/ti/wlcore/io.c
+++ b/drivers/net/wireless/ti/wlcore/io.c
@@ -60,6 +60,12 @@ void wlcore_enable_interrupts(struct wl1271 *wl)
60} 60}
61EXPORT_SYMBOL_GPL(wlcore_enable_interrupts); 61EXPORT_SYMBOL_GPL(wlcore_enable_interrupts);
62 62
63void wlcore_synchronize_interrupts(struct wl1271 *wl)
64{
65 synchronize_irq(wl->irq);
66}
67EXPORT_SYMBOL_GPL(wlcore_synchronize_interrupts);
68
63int wlcore_translate_addr(struct wl1271 *wl, int addr) 69int wlcore_translate_addr(struct wl1271 *wl, int addr)
64{ 70{
65 struct wlcore_partition_set *part = &wl->curr_part; 71 struct wlcore_partition_set *part = &wl->curr_part;
diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h
index fef80adc8bf5..458da5584533 100644
--- a/drivers/net/wireless/ti/wlcore/io.h
+++ b/drivers/net/wireless/ti/wlcore/io.h
@@ -47,6 +47,7 @@ struct wl1271;
47void wlcore_disable_interrupts(struct wl1271 *wl); 47void wlcore_disable_interrupts(struct wl1271 *wl);
48void wlcore_disable_interrupts_nosync(struct wl1271 *wl); 48void wlcore_disable_interrupts_nosync(struct wl1271 *wl);
49void wlcore_enable_interrupts(struct wl1271 *wl); 49void wlcore_enable_interrupts(struct wl1271 *wl);
50void wlcore_synchronize_interrupts(struct wl1271 *wl);
50 51
51void wl1271_io_reset(struct wl1271 *wl); 52void wl1271_io_reset(struct wl1271 *wl);
52void wl1271_io_init(struct wl1271 *wl); 53void wl1271_io_init(struct wl1271 *wl);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 575d18cfc832..641b0c9cd494 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -62,7 +62,7 @@ static bool no_recovery;
62static void __wl1271_op_remove_interface(struct wl1271 *wl, 62static void __wl1271_op_remove_interface(struct wl1271 *wl,
63 struct ieee80211_vif *vif, 63 struct ieee80211_vif *vif,
64 bool reset_tx_queues); 64 bool reset_tx_queues);
65static void wl1271_op_stop(struct ieee80211_hw *hw); 65static void wlcore_op_stop_locked(struct wl1271 *wl);
66static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); 66static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
67 67
68static int wl12xx_set_authorized(struct wl1271 *wl, 68static int wl12xx_set_authorized(struct wl1271 *wl,
@@ -956,9 +956,8 @@ static void wl1271_recovery_work(struct work_struct *work)
956 vif = wl12xx_wlvif_to_vif(wlvif); 956 vif = wl12xx_wlvif_to_vif(wlvif);
957 __wl1271_op_remove_interface(wl, vif, false); 957 __wl1271_op_remove_interface(wl, vif, false);
958 } 958 }
959 wl->watchdog_recovery = false; 959
960 mutex_unlock(&wl->mutex); 960 wlcore_op_stop_locked(wl);
961 wl1271_op_stop(wl->hw);
962 961
963 ieee80211_restart_hw(wl->hw); 962 ieee80211_restart_hw(wl->hw);
964 963
@@ -967,7 +966,7 @@ static void wl1271_recovery_work(struct work_struct *work)
967 * to restart the HW. 966 * to restart the HW.
968 */ 967 */
969 wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); 968 wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
970 return; 969
971out_unlock: 970out_unlock:
972 wl->watchdog_recovery = false; 971 wl->watchdog_recovery = false;
973 clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); 972 clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
@@ -1800,33 +1799,15 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
1800 return 0; 1799 return 0;
1801} 1800}
1802 1801
1803static void wl1271_op_stop(struct ieee80211_hw *hw) 1802static void wlcore_op_stop_locked(struct wl1271 *wl)
1804{ 1803{
1805 struct wl1271 *wl = hw->priv;
1806 int i; 1804 int i;
1807 1805
1808 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
1809
1810 /*
1811 * Interrupts must be disabled before setting the state to OFF.
1812 * Otherwise, the interrupt handler might be called and exit without
1813 * reading the interrupt status.
1814 */
1815 wlcore_disable_interrupts(wl);
1816 mutex_lock(&wl->mutex);
1817 if (wl->state == WL1271_STATE_OFF) { 1806 if (wl->state == WL1271_STATE_OFF) {
1818 if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, 1807 if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
1819 &wl->flags)) 1808 &wl->flags))
1820 wlcore_enable_interrupts(wl); 1809 wlcore_enable_interrupts(wl);
1821 1810
1822 mutex_unlock(&wl->mutex);
1823
1824 /*
1825 * This will not necessarily enable interrupts as interrupts
1826 * may have been disabled when op_stop was called. It will,
1827 * however, balance the above call to disable_interrupts().
1828 */
1829 wlcore_enable_interrupts(wl);
1830 return; 1811 return;
1831 } 1812 }
1832 1813
@@ -1835,8 +1816,16 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
1835 * functions don't perform further work. 1816 * functions don't perform further work.
1836 */ 1817 */
1837 wl->state = WL1271_STATE_OFF; 1818 wl->state = WL1271_STATE_OFF;
1819
1820 /*
1821 * Use the nosync variant to disable interrupts, so the mutex could be
1822 * held while doing so without deadlocking.
1823 */
1824 wlcore_disable_interrupts_nosync(wl);
1825
1838 mutex_unlock(&wl->mutex); 1826 mutex_unlock(&wl->mutex);
1839 1827
1828 wlcore_synchronize_interrupts(wl);
1840 wl1271_flush_deferred_work(wl); 1829 wl1271_flush_deferred_work(wl);
1841 cancel_delayed_work_sync(&wl->scan_complete_work); 1830 cancel_delayed_work_sync(&wl->scan_complete_work);
1842 cancel_work_sync(&wl->netstack_work); 1831 cancel_work_sync(&wl->netstack_work);
@@ -1903,6 +1892,17 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
1903 wl->tx_res_if = NULL; 1892 wl->tx_res_if = NULL;
1904 kfree(wl->target_mem_map); 1893 kfree(wl->target_mem_map);
1905 wl->target_mem_map = NULL; 1894 wl->target_mem_map = NULL;
1895}
1896
1897static void wlcore_op_stop(struct ieee80211_hw *hw)
1898{
1899 struct wl1271 *wl = hw->priv;
1900
1901 wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
1902
1903 mutex_lock(&wl->mutex);
1904
1905 wlcore_op_stop_locked(wl);
1906 1906
1907 mutex_unlock(&wl->mutex); 1907 mutex_unlock(&wl->mutex);
1908} 1908}
@@ -4806,7 +4806,7 @@ static struct ieee80211_supported_band wl1271_band_5ghz = {
4806 4806
4807static const struct ieee80211_ops wl1271_ops = { 4807static const struct ieee80211_ops wl1271_ops = {
4808 .start = wl1271_op_start, 4808 .start = wl1271_op_start,
4809 .stop = wl1271_op_stop, 4809 .stop = wlcore_op_stop,
4810 .add_interface = wl1271_op_add_interface, 4810 .add_interface = wl1271_op_add_interface,
4811 .remove_interface = wl1271_op_remove_interface, 4811 .remove_interface = wl1271_op_remove_interface,
4812 .change_interface = wl12xx_op_change_interface, 4812 .change_interface = wl12xx_op_change_interface,