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>
This commit is contained in:
parent
145cc1214a
commit
ec5ba4d3b6
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue