aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>2012-07-17 07:46:42 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-07-17 15:11:40 -0400
commit124b979baeb2d7a0593be8d392f43725578478c1 (patch)
tree3b6f3d1d1915f3dba8f2405a8e79f1bf7958b22e /drivers
parent6dcc344469d60a1f0d72cc638967e8c83c6e166e (diff)
ath9k: Fix race in reset-work usage
Using work_pending() to defer certain operations when a HW-reset work has been queued is racy since the check would return false when the work item is actually in execution. Use SC_OP_HW_RESET instead to fix this race. Also, unify the reset debug statistics maintenance. Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com> Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h24
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c13
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c17
-rw-r--r--drivers/net/wireless/ath/ath9k/mci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c13
7 files changed, 41 insertions, 34 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index c8af0db97c4f..b09285c36c4a 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -452,6 +452,7 @@ void ath_stop_ani(struct ath_softc *sc);
452void ath_check_ani(struct ath_softc *sc); 452void ath_check_ani(struct ath_softc *sc);
453int ath_update_survey_stats(struct ath_softc *sc); 453int ath_update_survey_stats(struct ath_softc *sc);
454void ath_update_survey_nf(struct ath_softc *sc, int channel); 454void ath_update_survey_nf(struct ath_softc *sc, int channel);
455void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
455 456
456/**********/ 457/**********/
457/* BTCOEX */ 458/* BTCOEX */
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 006ae99d2f59..76f07d8c272d 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -317,11 +317,12 @@ void ath9k_beacon_tasklet(unsigned long data)
317 bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); 317 bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
318 int slot; 318 int slot;
319 319
320 if (work_pending(&sc->hw_reset_work)) { 320 if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
321 ath_dbg(common, RESET, 321 ath_dbg(common, RESET,
322 "reset work is pending, skip beaconing now\n"); 322 "reset work is pending, skip beaconing now\n");
323 return; 323 return;
324 } 324 }
325
325 /* 326 /*
326 * Check if the previous beacon has gone out. If 327 * Check if the previous beacon has gone out. If
327 * not don't try to post another, skip this period 328 * not don't try to post another, skip this period
@@ -345,7 +346,7 @@ void ath9k_beacon_tasklet(unsigned long data)
345 } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { 346 } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
346 ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); 347 ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
347 sc->beacon.bmisscnt = 0; 348 sc->beacon.bmisscnt = 0;
348 ieee80211_queue_work(sc->hw, &sc->hw_reset_work); 349 ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK);
349 } 350 }
350 351
351 return; 352 return;
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index d0f851cea43a..8b9d080d89da 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -32,6 +32,19 @@ struct ath_buf;
32#define RESET_STAT_INC(sc, type) do { } while (0) 32#define RESET_STAT_INC(sc, type) do { } while (0)
33#endif 33#endif
34 34
35enum ath_reset_type {
36 RESET_TYPE_BB_HANG,
37 RESET_TYPE_BB_WATCHDOG,
38 RESET_TYPE_FATAL_INT,
39 RESET_TYPE_TX_ERROR,
40 RESET_TYPE_TX_HANG,
41 RESET_TYPE_PLL_HANG,
42 RESET_TYPE_MAC_HANG,
43 RESET_TYPE_BEACON_STUCK,
44 RESET_TYPE_MCI,
45 __RESET_TYPE_MAX
46};
47
35#ifdef CONFIG_ATH9K_DEBUGFS 48#ifdef CONFIG_ATH9K_DEBUGFS
36 49
37/** 50/**
@@ -209,17 +222,6 @@ struct ath_rx_stats {
209 u32 rx_frags; 222 u32 rx_frags;
210}; 223};
211 224
212enum ath_reset_type {
213 RESET_TYPE_BB_HANG,
214 RESET_TYPE_BB_WATCHDOG,
215 RESET_TYPE_FATAL_INT,
216 RESET_TYPE_TX_ERROR,
217 RESET_TYPE_TX_HANG,
218 RESET_TYPE_PLL_HANG,
219 RESET_TYPE_MAC_HANG,
220 __RESET_TYPE_MAX
221};
222
223struct ath_stats { 225struct ath_stats {
224 struct ath_interrupt_stats istats; 226 struct ath_interrupt_stats istats;
225 struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; 227 struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 42fc0a374c61..d4549e9aac5c 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -50,8 +50,7 @@ void ath_tx_complete_poll_work(struct work_struct *work)
50 if (needreset) { 50 if (needreset) {
51 ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, 51 ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
52 "tx hung, resetting the chip\n"); 52 "tx hung, resetting the chip\n");
53 RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); 53 ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
54 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
55 return; 54 return;
56 } 55 }
57 56
@@ -69,6 +68,7 @@ void ath_hw_check(struct work_struct *work)
69 unsigned long flags; 68 unsigned long flags;
70 int busy; 69 int busy;
71 u8 is_alive, nbeacon = 1; 70 u8 is_alive, nbeacon = 1;
71 enum ath_reset_type type;
72 72
73 ath9k_ps_wakeup(sc); 73 ath9k_ps_wakeup(sc);
74 is_alive = ath9k_hw_check_alive(sc->sc_ah); 74 is_alive = ath9k_hw_check_alive(sc->sc_ah);
@@ -78,7 +78,7 @@ void ath_hw_check(struct work_struct *work)
78 else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { 78 else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
79 ath_dbg(common, RESET, 79 ath_dbg(common, RESET,
80 "DCU stuck is detected. Schedule chip reset\n"); 80 "DCU stuck is detected. Schedule chip reset\n");
81 RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG); 81 type = RESET_TYPE_MAC_HANG;
82 goto sched_reset; 82 goto sched_reset;
83 } 83 }
84 84
@@ -90,7 +90,7 @@ void ath_hw_check(struct work_struct *work)
90 busy, sc->hw_busy_count + 1); 90 busy, sc->hw_busy_count + 1);
91 if (busy >= 99) { 91 if (busy >= 99) {
92 if (++sc->hw_busy_count >= 3) { 92 if (++sc->hw_busy_count >= 3) {
93 RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); 93 type = RESET_TYPE_BB_HANG;
94 goto sched_reset; 94 goto sched_reset;
95 } 95 }
96 } else if (busy >= 0) { 96 } else if (busy >= 0) {
@@ -102,7 +102,7 @@ void ath_hw_check(struct work_struct *work)
102 goto out; 102 goto out;
103 103
104sched_reset: 104sched_reset:
105 ieee80211_queue_work(sc->hw, &sc->hw_reset_work); 105 ath9k_queue_reset(sc, type);
106out: 106out:
107 ath9k_ps_restore(sc); 107 ath9k_ps_restore(sc);
108} 108}
@@ -119,8 +119,7 @@ static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
119 count++; 119 count++;
120 if (count == 3) { 120 if (count == 3) {
121 ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); 121 ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
122 RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); 122 ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG);
123 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
124 count = 0; 123 count = 0;
125 return true; 124 return true;
126 } 125 }
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 1caaf922ba9c..6049d8b82855 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -363,6 +363,7 @@ void ath9k_tasklet(unsigned long data)
363 struct ath_softc *sc = (struct ath_softc *)data; 363 struct ath_softc *sc = (struct ath_softc *)data;
364 struct ath_hw *ah = sc->sc_ah; 364 struct ath_hw *ah = sc->sc_ah;
365 struct ath_common *common = ath9k_hw_common(ah); 365 struct ath_common *common = ath9k_hw_common(ah);
366 enum ath_reset_type type;
366 unsigned long flags; 367 unsigned long flags;
367 u32 status = sc->intrstatus; 368 u32 status = sc->intrstatus;
368 u32 rxmask; 369 u32 rxmask;
@@ -372,18 +373,13 @@ void ath9k_tasklet(unsigned long data)
372 373
373 if ((status & ATH9K_INT_FATAL) || 374 if ((status & ATH9K_INT_FATAL) ||
374 (status & ATH9K_INT_BB_WATCHDOG)) { 375 (status & ATH9K_INT_BB_WATCHDOG)) {
375#ifdef CONFIG_ATH9K_DEBUGFS
376 enum ath_reset_type type;
377 376
378 if (status & ATH9K_INT_FATAL) 377 if (status & ATH9K_INT_FATAL)
379 type = RESET_TYPE_FATAL_INT; 378 type = RESET_TYPE_FATAL_INT;
380 else 379 else
381 type = RESET_TYPE_BB_WATCHDOG; 380 type = RESET_TYPE_BB_WATCHDOG;
382 381
383 RESET_STAT_INC(sc, type); 382 ath9k_queue_reset(sc, type);
384#endif
385 set_bit(SC_OP_HW_RESET, &sc->sc_flags);
386 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
387 goto out; 383 goto out;
388 } 384 }
389 385
@@ -584,6 +580,15 @@ static int ath_reset(struct ath_softc *sc, bool retry_tx)
584 return r; 580 return r;
585} 581}
586 582
583void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
584{
585#ifdef CONFIG_ATH9K_DEBUGFS
586 RESET_STAT_INC(sc, type);
587#endif
588 set_bit(SC_OP_HW_RESET, &sc->sc_flags);
589 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
590}
591
587void ath_reset_work(struct work_struct *work) 592void ath_reset_work(struct work_struct *work)
588{ 593{
589 struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work); 594 struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index 87acff7fdaae..fb536e7e661b 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -202,7 +202,7 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)
202 case MCI_GPM_BT_CAL_REQ: 202 case MCI_GPM_BT_CAL_REQ:
203 if (mci_hw->bt_state == MCI_BT_AWAKE) { 203 if (mci_hw->bt_state == MCI_BT_AWAKE) {
204 ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START); 204 ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START);
205 ieee80211_queue_work(sc->hw, &sc->hw_reset_work); 205 ath9k_queue_reset(sc, RESET_TYPE_MCI);
206 } 206 }
207 ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); 207 ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state);
208 break; 208 break;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 310c95e33cb1..2c9da6b2ecb1 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -589,10 +589,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
589 589
590 rcu_read_unlock(); 590 rcu_read_unlock();
591 591
592 if (needreset) { 592 if (needreset)
593 RESET_STAT_INC(sc, RESET_TYPE_TX_ERROR); 593 ath9k_queue_reset(sc, RESET_TYPE_TX_ERROR);
594 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
595 }
596} 594}
597 595
598static bool ath_lookup_legacy(struct ath_buf *bf) 596static bool ath_lookup_legacy(struct ath_buf *bf)
@@ -1589,7 +1587,8 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
1589 struct ath_atx_ac *ac, *ac_tmp, *last_ac; 1587 struct ath_atx_ac *ac, *ac_tmp, *last_ac;
1590 struct ath_atx_tid *tid, *last_tid; 1588 struct ath_atx_tid *tid, *last_tid;
1591 1589
1592 if (work_pending(&sc->hw_reset_work) || list_empty(&txq->axq_acq) || 1590 if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) ||
1591 list_empty(&txq->axq_acq) ||
1593 txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) 1592 txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
1594 return; 1593 return;
1595 1594
@@ -2196,7 +2195,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
2196 2195
2197 ath_txq_lock(sc, txq); 2196 ath_txq_lock(sc, txq);
2198 for (;;) { 2197 for (;;) {
2199 if (work_pending(&sc->hw_reset_work)) 2198 if (test_bit(SC_OP_HW_RESET, &sc->sc_flags))
2200 break; 2199 break;
2201 2200
2202 if (list_empty(&txq->axq_q)) { 2201 if (list_empty(&txq->axq_q)) {
@@ -2279,7 +2278,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
2279 int status; 2278 int status;
2280 2279
2281 for (;;) { 2280 for (;;) {
2282 if (work_pending(&sc->hw_reset_work)) 2281 if (test_bit(SC_OP_HW_RESET, &sc->sc_flags))
2283 break; 2282 break;
2284 2283
2285 status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts); 2284 status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts);