Patch "ath9k: Fix potential interrupt storm on queue reset" has been added to the 4.14-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    ath9k: Fix potential interrupt storm on queue reset

to the 4.14-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     ath9k-fix-potential-interrupt-storm-on-queue-reset.patch
and it can be found in the queue-4.14 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit d99c0aa2289876f8824470a5a6ff7fc8beb0ba01
Author: Linus Lüssing <ll@xxxxxxxxxxxxxxxxxx>
Date:   Tue Oct 5 16:55:53 2021 +0300

    ath9k: Fix potential interrupt storm on queue reset
    
    [ Upstream commit 4925642d541278575ad1948c5924d71ffd57ef14 ]
    
    In tests with two Lima boards from 8devices (QCA4531 based) on OpenWrt
    19.07 we could force a silent restart of a device with no serial
    output when we were sending a high amount of UDP traffic (iperf3 at 80
    MBit/s in both directions from external hosts, saturating the wifi and
    causing a load of about 4.5 to 6) and were then triggering an
    ath9k_queue_reset().
    
    Further debugging showed that the restart was caused by the ath79
    watchdog. With disabled watchdog we could observe that the device was
    constantly going into ath_isr() interrupt handler and was returning
    early after the ATH_OP_HW_RESET flag test, without clearing any
    interrupts. Even though ath9k_queue_reset() calls
    ath9k_hw_kill_interrupts().
    
    With JTAG we could observe the following race condition:
    
    1) ath9k_queue_reset()
       ...
       -> ath9k_hw_kill_interrupts()
       -> set_bit(ATH_OP_HW_RESET, &common->op_flags);
       ...
       <- returns
    
          2) ath9k_tasklet()
             ...
             -> ath9k_hw_resume_interrupts()
             ...
             <- returns
    
                     3) loops around:
                        ...
                        handle_int()
                        -> ath_isr()
                           ...
                           -> if (test_bit(ATH_OP_HW_RESET,
                                           &common->op_flags))
                                return IRQ_HANDLED;
    
                        x) ath_reset_internal():
                           => never reached <=
    
    And in ath_isr() we would typically see the following interrupts /
    interrupt causes:
    
    * status: 0x00111030 or 0x00110030
    * async_cause: 2 (AR_INTR_MAC_IPQ)
    * sync_cause: 0
    
    So the ath9k_tasklet() reenables the ath9k interrupts
    through ath9k_hw_resume_interrupts() which ath9k_queue_reset() had just
    disabled. And ath_isr() then keeps firing because it returns IRQ_HANDLED
    without actually clearing the interrupt.
    
    To fix this IRQ storm also clear/disable the interrupts again when we
    are in reset state.
    
    Cc: Sven Eckelmann <sven@xxxxxxxxxxxxx>
    Cc: Simon Wunderlich <sw@xxxxxxxxxxxxxxxxxx>
    Cc: Linus Lüssing <linus.luessing@xxxxxxxxx>
    Fixes: 872b5d814f99 ("ath9k: do not access hardware on IRQs during reset")
    Signed-off-by: Linus Lüssing <ll@xxxxxxxxxxxxxxxxxx>
    Signed-off-by: Kalle Valo <kvalo@xxxxxxxxxxxxxx>
    Link: https://lore.kernel.org/r/20210914192515.9273-3-linus.luessing@xxxxxxxxx
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 173960682ea06..507d8c5149686 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -529,8 +529,10 @@ irqreturn_t ath_isr(int irq, void *dev)
 	ath9k_debug_sync_cause(sc, sync_cause);
 	status &= ah->imask;	/* discard unasked-for bits */
 
-	if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
+	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
+		ath9k_hw_kill_interrupts(sc->sc_ah);
 		return IRQ_HANDLED;
+	}
 
 	/*
 	 * If there are no status bits set, then this interrupt was not



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux