From 46b0cc9ffce356fbcb2135219a3ed8500714a9e4 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 11 Jan 2012 09:42:41 +0200 Subject: wl12xx: Fix potential interrupt storm The interrupt threaded handler exits immediately if the driver's state is WL1271_STATE_OFF. As a result, the interrupt status is not read. If the interrupt is level triggered, it will be fired again. Fix this by disabling interrupts before setting the state to OFF. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9e9fb3f..3a1636b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1397,9 +1397,23 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_notice("power down"); + /* + * Interrupts must be disabled before setting the state to OFF. + * Otherwise, the interrupt handler might be called and exit without + * reading the interrupt status. + */ + wl1271_disable_interrupts(wl); mutex_lock(&wl->mutex); if (wl->state != WL1271_STATE_PLT) { mutex_unlock(&wl->mutex); + + /* + * This will not necessarily enable interrupts as interrupts + * may have been disabled when op_stop was called. It will, + * however, balance the above call to disable_interrupts(). + */ + wl1271_enable_interrupts(wl); + wl1271_error("cannot power down because not in PLT " "state: %d", wl->state); ret = -EBUSY; @@ -1411,7 +1425,6 @@ int wl1271_plt_stop(struct wl1271 *wl) mutex_unlock(&wl->mutex); - wl1271_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); @@ -1773,11 +1786,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + /* + * Interrupts must be disabled before setting the state to OFF. + * Otherwise, the interrupt handler might be called and exit without + * reading the interrupt status. + */ + wl1271_disable_interrupts(wl); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { mutex_unlock(&wl->mutex); + + /* + * This will not necessarily enable interrupts as interrupts + * may have been disabled when op_stop was called. It will, + * however, balance the above call to disable_interrupts(). + */ + wl1271_enable_interrupts(wl); return; } + /* * this must be before the cancel_work calls below, so that the work * functions don't perform further work. @@ -1789,7 +1816,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) list_del(&wl->list); mutex_unlock(&wl_list_mutex); - wl1271_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_delayed_work_sync(&wl->scan_complete_work); cancel_work_sync(&wl->netstack_work); -- cgit v0.10.2