ath9k: Set ATH_OP_HW_RESET before HW reset
authorSujith Manoharan <c_manoha@qca.qualcomm.com>
Fri, 17 Oct 2014 02:10:12 +0000 (07:40 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 27 Oct 2014 18:16:14 +0000 (14:16 -0400)
When a HW reset is done, the interrupt tasklet is
disabled before ISRs are disabled in the HW. This
allows a small window where the HW can still generate
interrupts. Since the tasklet is disabled and not killed,
it is not scheduled but deferred for execution at a later
time.

This happens because ATH_OP_HW_RESET is not set when ath_reset()
is called. When the hw_reset_work workqueue is used, this
problem doesn't arise because ATH_OP_HW_RESET is set
and the ISR bails out.

Set ATH_OP_HW_RESET properly in ath_reset() to avoid
this race - all the ath_reset_internal() callers have
been converted to use ath_reset() in the previous patch.

Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/main.c

index eb6ba9f8d9ff0a77b41a9e52a3434241835d4a20..dda09ba321d7b37c8bda7b4fcaa8c5ec53f8815e 100644 (file)
@@ -598,10 +598,17 @@ chip_reset:
 #undef SCHED_INTR
 }
 
+/*
+ * This function is called when a HW reset cannot be deferred
+ * and has to be immediate.
+ */
 int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
 {
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int r;
 
+       set_bit(ATH_OP_HW_RESET, &common->op_flags);
+
        ath9k_ps_wakeup(sc);
        r = ath_reset_internal(sc, hchan);
        ath9k_ps_restore(sc);
@@ -609,6 +616,11 @@ int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
        return r;
 }
 
+/*
+ * When a HW reset can be deferred, it is added to the
+ * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before
+ * queueing.
+ */
 void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);