aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Kazior2014-08-22 14:23:33 +0200
committerKalle Valo2014-08-25 11:28:56 +0300
commitec5ba4d3b6b60456b067e8c625e87e67cdde2d12 (patch)
tree5efdd7d536de18235039627f75f009aff06c2738
parent145cc1214a271c72b81a064f4d65c3cf612e941e (diff)
ath10k: make sure to really disable irqs
This fixes two corner cases. One is a race between disabling copy engine interrupts and unhandled pending interrupts on the host. This could end up with a runaway tasklet and consequently memory leak of a few copy engine rx buffers. The other one is an unexpected (and non-maskable via device CSR) MSI fw indication interrupt during teardown. This could trigger the same problem as the first corner case. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c43
1 files changed, 37 insertions, 6 deletions
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index c764dd713f00..6224952ba523 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1121,6 +1121,40 @@ static int ath10k_pci_post_rx(struct ath10k *ar)
return 0;
}
+static void ath10k_pci_irq_disable(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ int i;
+
+ ath10k_ce_disable_interrupts(ar);
+
+ /* Regardless how many interrupts were assigned for MSI the first one
+ * is always used for firmware indications (crashes). There's no way to
+ * mask the irq in the device so call disable_irq(). Legacy (shared)
+ * interrupts can be masked on the device though.
+ */
+ if (ar_pci->num_msi_intrs > 0)
+ disable_irq(ar_pci->pdev->irq);
+ else
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+
+ for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+ synchronize_irq(ar_pci->pdev->irq + i);
+}
+
+static void ath10k_pci_irq_enable(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ ath10k_ce_enable_interrupts(ar);
+
+ /* See comment in ath10k_pci_irq_disable() */
+ if (ar_pci->num_msi_intrs > 0)
+ enable_irq(ar_pci->pdev->irq);
+ else
+ ath10k_pci_enable_legacy_irq(ar);
+}
+
static int ath10k_pci_hif_start(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -1138,7 +1172,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
goto err_early_irq;
}
- ath10k_ce_enable_interrupts(ar);
+ ath10k_pci_irq_enable(ar);
/* Post buffers once to start things off. */
ret = ath10k_pci_post_rx(ar);
@@ -1152,7 +1186,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
return 0;
err_stop:
- ath10k_ce_disable_interrupts(ar);
+ ath10k_pci_irq_disable(ar);
ath10k_pci_free_irq(ar);
ath10k_pci_kill_tasklet(ar);
err_early_irq:
@@ -1275,10 +1309,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
if (WARN_ON(!ar_pci->started))
return;
- ret = ath10k_ce_disable_interrupts(ar);
- if (ret)
- ath10k_warn("failed to disable CE interrupts: %d\n", ret);
-
+ ath10k_pci_irq_disable(ar);
ath10k_pci_free_irq(ar);
ath10k_pci_kill_tasklet(ar);