diff options
author | Ido Yariv <ido@wizery.com> | 2012-01-11 07:42:41 (GMT) |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2012-02-15 06:38:28 (GMT) |
commit | 46b0cc9ffce356fbcb2135219a3ed8500714a9e4 (patch) | |
tree | 3f7af09f3b7ff2380701eeb0764e170903182765 /drivers/net | |
parent | a454969ede024a209e6a755d5f7a928ab81ed00c (diff) | |
download | linux-fsl-qoriq-46b0cc9ffce356fbcb2135219a3ed8500714a9e4.tar.xz |
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 <ido@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 30 |
1 files changed, 28 insertions, 2 deletions
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); |