aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBob Copeland <me@bobcopeland.com>2010-07-13 11:32:40 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-07-14 13:52:46 -0400
commit5faaff747710dfb79d5aa72b9faface94ad4b3f3 (patch)
treefaddf5250f62abfe8d9b9f96a53712ca3db3e737 /drivers
parentda5747eb89eb1511dcfc1d8b32c70370616eac92 (diff)
ath5k: move reset to mac80211 workqueue
We currently trigger a reset via a tasklet when certain error conditions are detected so that the card will (eventually) restart. Unfortunately this makes locking complicated since reset can also be called in process context (e.g. for channel change). Currently nothing protects against concurrent resets, which can be the source of corruption bugs. Reset takes too long to spinlock the whole thing, so this patch moves deferred resets into the mac80211 workqueue to enable use of sc->lock mutex. Signed-off-by: Bob Copeland <me@bobcopeland.com> Acked-by: Bruno Randolf <br1@einfach.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c38
-rw-r--r--drivers/net/wireless/ath/ath5k/base.h3
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c2
3 files changed, 24 insertions, 19 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 20328bdd138b..b290cc6c600f 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -388,7 +388,7 @@ static int ath5k_init(struct ath5k_softc *sc);
388static int ath5k_stop_locked(struct ath5k_softc *sc); 388static int ath5k_stop_locked(struct ath5k_softc *sc);
389static int ath5k_stop_hw(struct ath5k_softc *sc); 389static int ath5k_stop_hw(struct ath5k_softc *sc);
390static irqreturn_t ath5k_intr(int irq, void *dev_id); 390static irqreturn_t ath5k_intr(int irq, void *dev_id);
391static void ath5k_tasklet_reset(unsigned long data); 391static void ath5k_reset_work(struct work_struct *work);
392 392
393static void ath5k_tasklet_calibrate(unsigned long data); 393static void ath5k_tasklet_calibrate(unsigned long data);
394 394
@@ -831,11 +831,12 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
831 831
832 tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); 832 tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
833 tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); 833 tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
834 tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
835 tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc); 834 tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
836 tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); 835 tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
837 tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); 836 tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
838 837
838 INIT_WORK(&sc->reset_work, ath5k_reset_work);
839
839 ret = ath5k_eeprom_read_mac(ah, mac); 840 ret = ath5k_eeprom_read_mac(ah, mac);
840 if (ret) { 841 if (ret) {
841 ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n", 842 ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
@@ -2294,8 +2295,8 @@ err_unmap:
2294 * frame contents are done as needed and the slot time is 2295 * frame contents are done as needed and the slot time is
2295 * also adjusted based on current state. 2296 * also adjusted based on current state.
2296 * 2297 *
2297 * This is called from software irq context (beacontq or restq 2298 * This is called from software irq context (beacontq tasklets)
2298 * tasklets) or user context from ath5k_beacon_config. 2299 * or user context from ath5k_beacon_config.
2299 */ 2300 */
2300static void 2301static void
2301ath5k_beacon_send(struct ath5k_softc *sc) 2302ath5k_beacon_send(struct ath5k_softc *sc)
@@ -2328,7 +2329,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
2328 sc->bmisscount); 2329 sc->bmisscount);
2329 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, 2330 ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
2330 "stuck beacon, resetting\n"); 2331 "stuck beacon, resetting\n");
2331 tasklet_schedule(&sc->restq); 2332 ieee80211_queue_work(sc->hw, &sc->reset_work);
2332 } 2333 }
2333 return; 2334 return;
2334 } 2335 }
@@ -2684,7 +2685,6 @@ ath5k_stop_hw(struct ath5k_softc *sc)
2684 2685
2685 tasklet_kill(&sc->rxtq); 2686 tasklet_kill(&sc->rxtq);
2686 tasklet_kill(&sc->txtq); 2687 tasklet_kill(&sc->txtq);
2687 tasklet_kill(&sc->restq);
2688 tasklet_kill(&sc->calib); 2688 tasklet_kill(&sc->calib);
2689 tasklet_kill(&sc->beacontq); 2689 tasklet_kill(&sc->beacontq);
2690 tasklet_kill(&sc->ani_tasklet); 2690 tasklet_kill(&sc->ani_tasklet);
@@ -2737,7 +2737,7 @@ ath5k_intr(int irq, void *dev_id)
2737 */ 2737 */
2738 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, 2738 ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
2739 "fatal int, resetting\n"); 2739 "fatal int, resetting\n");
2740 tasklet_schedule(&sc->restq); 2740 ieee80211_queue_work(sc->hw, &sc->reset_work);
2741 } else if (unlikely(status & AR5K_INT_RXORN)) { 2741 } else if (unlikely(status & AR5K_INT_RXORN)) {
2742 /* 2742 /*
2743 * Receive buffers are full. Either the bus is busy or 2743 * Receive buffers are full. Either the bus is busy or
@@ -2752,7 +2752,7 @@ ath5k_intr(int irq, void *dev_id)
2752 if (ah->ah_mac_srev < AR5K_SREV_AR5212) { 2752 if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
2753 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, 2753 ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
2754 "rx overrun, resetting\n"); 2754 "rx overrun, resetting\n");
2755 tasklet_schedule(&sc->restq); 2755 ieee80211_queue_work(sc->hw, &sc->reset_work);
2756 } 2756 }
2757 else 2757 else
2758 tasklet_schedule(&sc->rxtq); 2758 tasklet_schedule(&sc->rxtq);
@@ -2799,14 +2799,6 @@ ath5k_intr(int irq, void *dev_id)
2799 return IRQ_HANDLED; 2799 return IRQ_HANDLED;
2800} 2800}
2801 2801
2802static void
2803ath5k_tasklet_reset(unsigned long data)
2804{
2805 struct ath5k_softc *sc = (void *)data;
2806
2807 ath5k_reset(sc, sc->curchan);
2808}
2809
2810/* 2802/*
2811 * Periodically recalibrate the PHY to account 2803 * Periodically recalibrate the PHY to account
2812 * for temperature/environment changes. 2804 * for temperature/environment changes.
@@ -2830,7 +2822,7 @@ ath5k_tasklet_calibrate(unsigned long data)
2830 * to load new gain values. 2822 * to load new gain values.
2831 */ 2823 */
2832 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n"); 2824 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
2833 ath5k_reset(sc, sc->curchan); 2825 ieee80211_queue_work(sc->hw, &sc->reset_work);
2834 } 2826 }
2835 if (ath5k_hw_phy_calibrate(ah, sc->curchan)) 2827 if (ath5k_hw_phy_calibrate(ah, sc->curchan))
2836 ATH5K_ERR(sc, "calibration of channel %u failed\n", 2828 ATH5K_ERR(sc, "calibration of channel %u failed\n",
@@ -2934,6 +2926,8 @@ drop_packet:
2934/* 2926/*
2935 * Reset the hardware. If chan is not NULL, then also pause rx/tx 2927 * Reset the hardware. If chan is not NULL, then also pause rx/tx
2936 * and change to the given channel. 2928 * and change to the given channel.
2929 *
2930 * This should be called with sc->lock.
2937 */ 2931 */
2938static int 2932static int
2939ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan) 2933ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
@@ -2990,6 +2984,16 @@ err:
2990 return ret; 2984 return ret;
2991} 2985}
2992 2986
2987static void ath5k_reset_work(struct work_struct *work)
2988{
2989 struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
2990 reset_work);
2991
2992 mutex_lock(&sc->lock);
2993 ath5k_reset(sc, sc->curchan);
2994 mutex_unlock(&sc->lock);
2995}
2996
2993static int ath5k_start(struct ieee80211_hw *hw) 2997static int ath5k_start(struct ieee80211_hw *hw)
2994{ 2998{
2995 return ath5k_init(hw->priv); 2999 return ath5k_init(hw->priv);
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index 56221bc7c8cd..86c90f471b74 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -47,6 +47,7 @@
47#include <linux/if_ether.h> 47#include <linux/if_ether.h>
48#include <linux/leds.h> 48#include <linux/leds.h>
49#include <linux/rfkill.h> 49#include <linux/rfkill.h>
50#include <linux/workqueue.h>
50 51
51#include "ath5k.h" 52#include "ath5k.h"
52#include "debug.h" 53#include "debug.h"
@@ -189,7 +190,7 @@ struct ath5k_softc {
189 unsigned int led_pin, /* GPIO pin for driving LED */ 190 unsigned int led_pin, /* GPIO pin for driving LED */
190 led_on; /* pin setting for LED on */ 191 led_on; /* pin setting for LED on */
191 192
192 struct tasklet_struct restq; /* reset tasklet */ 193 struct work_struct reset_work; /* deferred chip reset */
193 194
194 unsigned int rxbufsize; /* rx size based on mtu */ 195 unsigned int rxbufsize; /* rx size based on mtu */
195 struct list_head rxbuf; /* receive buffer */ 196 struct list_head rxbuf; /* receive buffer */
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 8c638865c712..ebb9c237a0d5 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -279,7 +279,7 @@ static ssize_t write_file_reset(struct file *file,
279{ 279{
280 struct ath5k_softc *sc = file->private_data; 280 struct ath5k_softc *sc = file->private_data;
281 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "debug file triggered reset\n"); 281 ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "debug file triggered reset\n");
282 tasklet_schedule(&sc->restq); 282 ieee80211_queue_work(sc->hw, &sc->reset_work);
283 return count; 283 return count;
284} 284}
285 285